Skip to content

Instantly share code, notes, and snippets.

@sarkahn
Created July 11, 2015 19:23
Show Gist options
  • Save sarkahn/57037c2a5d5c48a21992 to your computer and use it in GitHub Desktop.
Save sarkahn/57037c2a5d5c48a21992 to your computer and use it in GitHub Desktop.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine.EventSystems;
/// <summary>
/// Node Behaviour representing a node. Assumes it's parent has a Layout Group.
/// </summary>
[RequireComponent(typeof(Image)),RequireComponent(typeof(VerticalLayoutGroup))]
public class NodeBehaviour : MonoBehaviour, IPointerDownHandler
{
Node root_;
/// <summary>
/// Layout Group for child nodes. Will be a sibling of this NodeBehaviour's gameobject so
/// the nodes can be offset from the left visually using padding
/// </summary>
VerticalLayoutGroup childrenLayout_ = null;
public int indentSize_ = 10;
public int indentLevel_ = 1;
// Text object to represent the node. Created on awake
GameObject textGO_;
// Could be cached
public Text text_ { get { return textGO_.GetComponent<Text>(); } }
RectTransform rt_ { get { return GetComponent<RectTransform>(); } }
VerticalLayoutGroup layout_ { get { return GetComponent<VerticalLayoutGroup>(); } }
Image image_ { get { return GetComponent<Image>(); } }
void Awake()
{
// Create our text object and reparent it
textGO_ = new GameObject("Node text", typeof(Text));
text_.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
textGO_.transform.SetParent(transform, false);
textGO_.transform.localPosition = Vector3.zero;
// No expanding
layout_.childForceExpandHeight = false;
layout_.childForceExpandWidth = false;
}
// Populate our visuals and create the nodebehaviours for any children of this node
public void BuildVisualTree()
{
textGO_.name = root_.name_ + " Text";
text_.text = root_.name_;
image_.sprite = null;
image_.type = Image.Type.Sliced;
Color color = Color.grey;
color.a = .392f;
image_.color = color;
if (root_.children_.Count == 0)
return;
for (int i = 0; i < root_.children_.Count; ++i)
{
var behaviour = AddBehaviourNode(root_[i]);
behaviour.BuildVisualTree();
}
}
// Add a behaviour node as a child of this node, using the given node as the root for the new behaviour
NodeBehaviour AddBehaviourNode( Node n )
{
if (childrenLayout_ == null)
MakeChildrenLayout();
var nodeGO = new GameObject(n.name_, typeof(NodeBehaviour));
var nodeBehaviour = nodeGO.GetComponent<NodeBehaviour>();
// Increment our indent level so children will be offset appropriately
++nodeBehaviour.indentLevel_;
nodeBehaviour.root_ = n;
// Reparent the new child node to the childrenLayout
nodeGO.transform.SetParent(childrenLayout_.transform, false);
return nodeBehaviour;
}
// Left click to collapse a node, right click to add a new child to the node
public void OnPointerDown(PointerEventData eventData)
{
if( eventData.button == PointerEventData.InputButton.Left )
if( childrenLayout_ != null )
childrenLayout_.gameObject.SetActive(!childrenLayout_.gameObject.activeInHierarchy);
if( eventData.button == PointerEventData.InputButton.Right )
{
var newNode = new Node(root_.name_ + "-> dynamic " + (root_.children_.Count - 1) );
root_.children_.Add(newNode);
var newBehaviour = AddBehaviourNode(newNode);
newBehaviour.BuildVisualTree();
}
}
void MakeChildrenLayout()
{
// Create the layout group for our children. Note that it's created as a sibling of this nodebehaviour.
// That way we can use the padding of the childrenLayout and they will appear to be a sub-group of this
childrenLayout_ = new GameObject(name + " childrenLayout", typeof(VerticalLayoutGroup)).GetComponent<VerticalLayoutGroup>();
childrenLayout_.transform.SetParent(transform.parent, false);
// Ensure the childrenLayout is directly below this Node Behaviour
childrenLayout_.transform.SetSiblingIndex(transform.GetSiblingIndex() + 1);
childrenLayout_.padding.left = indentLevel_ * indentSize_;
childrenLayout_.childForceExpandHeight = false;
childrenLayout_.childForceExpandWidth = false;
}
void PopulateNode(Node n, int count)
{
n.children_.Clear();
for (int i = 0; i < count; ++i)
{
n.children_.Add(new Node(n.name_ + "->" + " child " + i));
}
}
void PopulateRoot()
{
PopulateNode( root_, 3 );
PopulateNode( root_[1], 2 );
PopulateNode(root_[1], 3);
PopulateNode(root_[1][1], 2);
}
[ContextMenu("RebuildVisualTree")]
void RebuildTree()
{
if( childrenLayout_ != null )
GameObject.DestroyImmediate(childrenLayout_);
root_ = new Node("Root");
PopulateRoot();
BuildVisualTree();
}
}
public class Node
{
public Node(string name) { name_ = name; }
public string name_;
public List<Node> children_ = new List<Node>();
public Node this[int i] { get { return children_[i]; } }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment