Hi all
I am playing around with ways to make a side menu for my project that lists gameObjects in a hierarchical way. So far I have come up with the code below, but it still needs work, which seems to be beyond my coding capabilities.
When attached to the ROOT object, the code below displays three levels Parents, Children and GrandChildren in the hierarchy. See the image attached to see how I organised the objects in the scene. I have just changed the “left padding” of each style to indent the hierarchy. So ParentStyle = 0 padding, ChildStyle = 30 padding and GrandChildStyle = 60 padding.
At the moment when I click on one level, the rest of that level opens. I would like to control this individually. I’d appreciate some help with this please.
var ParentStyle : GUIStyle;
var ChildStyle : GUIStyle;
var GrandChildStyle : GUIStyle;
var Parent : boolean;
var Child : boolean;
var GrandChild : boolean;
var root : Transform;
function OnGUI(){
//MakeParents
for(var Parents : Transform in root){
GUI.BeginGroup (new Rect (50, 50, 500, 600));
if (GUILayout.Button (Parent ? "-" + Parents.name : "+" + Parents.name, ParentStyle)){
Parent = !Parent;
}
if (Parent)
{
//MakeChildren
for(var Children in Parents){
if (GUILayout.Button (Child ? "-" + Children.name: "+" + Children.name, ChildStyle)){
Child = !Child;
}
if (Child)
{
//MakeGrandChildren
for(var GrandChildren in Children){
if (GUILayout.Button (GrandChild ? "-" + GrandChildren.name: "+" + GrandChildren.name, GrandChildStyle)){
GrandChild = !GrandChild;
}
if (GrandChild)
{
//do something
}
}
}
}
}
GUI.EndGroup ();
}
}

The tree menu is ideally represented by a tree data structure in the code. You could start with a class like this:-
class GUIEntry {
public var name: String;
public var indent: int;
public var trans: Transform;
public var open: boolean;
public var children: GUIEntry[];
}
The main thing to note about this is that, as well as the main data, it contains an array of the same class that is being declared (which is perfectly valid code). Trees are fundamentally about this so-called “recursive” style of definition, where an object contains a reference to either itself or another object of the same type. The function to populate the tree is also recursively defined:-
function BuildTree(root: Transform, indent: int): GUIEntry {
var entry: GUIEntry = new GUIEntry();
entry.name = name;
entry.indent = indent;
entry.trans = root;
entry.children = new GUIEntry[root.childCount];
var childIndex = 0;
for (child in root) {
entry.children[childIndex] = BuildTree(child, indent + 1);
childIndex++;
}
return entry;
}
Here, the recursion involves the function calling itself repeatedly with each of the root’s children as the parameter for each call. This tree structure is good for representing the object hierarchy but not so great for display purposes, for which you want a straightforward list. You can obtain a list from the tree structure using a so-called “traversal” operation (ie, visiting the tree elements in a methodical order):-
function FlatList(list: Array, root: GUIEntry) {
list.Add(root);
if (root.open) {
for (i = 0; i < root.children.Length; i++) {
FlatList(list, root.children[i]);
}
}
}
This adds elements to an array for display, missing out the ones whose “open” property is false. You can display the array by simply running through the array with a loop as you do in your original code. When the user opens or closes an item, change the “open” property of the appropriate element in the array and call FlatList again to rebuild the display array.
Thank you Andeeee for your reply.
I will attempt to implement your solution now. My primary goal is to become a better programmer, so may I ask you a follow up question? Why is your solution, implementing Classes and defining Functions is more correct?
While waiting for a reply to this thread, I have continued to tinker with the code that I had and came up with this… which almost, so very nearly does exactly what I want, except the plus and minus is unpredictable and when the parent object is closed it’s grandchildren (only in one instance) remain visible. So the question: Is your solution basically bulletproof and robust, while my code has a leak and therefore unpredictable?
Thanks again
var ParentStyle : GUIStyle;
var ChildStyle : GUIStyle;
var GrandChildStyle : GUIStyle;
var ParentPM : String;
var ChildPM : String;
var GrandChildPM : String;
var Plus : String = "+ ";
var Minus : String = "- ";
var root : Transform;
function OnGUI(){
GUI.BeginGroup (new Rect (50, 50, 500, 600));
//MakeParents
for (var Parents : Transform in root){
Parents.active = GUILayout.Toggle (Parents.active, ParentPM + Parents.name, ParentStyle);
//MakeChildren
for(var Children in Parents){
if (!Parents.active)
{
Children.active = false;
ParentPM = Minus;
}
else
{
ParentPM = Plus;
Children.active = GUILayout.Toggle (Children.active, ChildPM + Children.name, ChildStyle);
//MakeGrandChildren
for(var GrandChildren in Children){
if (!Children.active)
{
GrandChildren.active = false;
ChildPM = Plus;
}
else
{
ChildPM = Minus;
GrandChildren.active = GUILayout.Toggle (GrandChildren.active, GrandChildPM + GrandChildren.name, GrandChildStyle);
}
}
}
}
}
GUI.EndGroup ();
}
For anyone who is interested, this code now does exactly what I need. It works, but the question remains, “is it correct”?
Are there many ways to skin the proverbial cat? Or are some coding practices better, more efficient, stable than others?
I’m still a complete NOOB at this so just interested in optimization, seeing what I may be doing right and wrong. Cheers.
var ParentStyle : GUIStyle;
var ChildStyle : GUIStyle;
var GrandChildStyle : GUIStyle;
var root : Transform;
function OnGUI(){
GUI.BeginGroup (new Rect (50, 50, 500, 600));
for (var Parents : Transform in root){
if (GUILayout.Button (Parents.active ? "-" + Parents.name : "+" + Parents.name, ParentStyle)){
Parents.active = !Parents.active;
}
if (!Parents.active)
{
for(var Children in Parents){
Children.active = false;
for(var GrandChildren in Children){
GrandChildren.active = false;
}
}
}
else
{
for(var Children in Parents){
if (GUILayout.Button (Children.active ? "-" + Children.name : "+" + Children.name, ChildStyle)){
Children.active = !Children.active;
}
if (!Children.active)
{
for(var GrandChildren in Children){
GrandChildren.active = false;
}
}
else
{
for(var GrandChildren in Children){
if (GUILayout.Button (GrandChildren.active ? "-" + GrandChildren.name : "+" + GrandChildren.name, GrandChildStyle)){
GrandChildren.active = !GrandChildren.active;
}
}
}
}
}
}
GUI.EndGroup ();
}
From what I can tell (and I don’t know how optimized either one is in the compiled byte code), Andeeee’s code seems cleaner and easier to understand given how he broke it into neat pieces, but also his code is infinitely recursive (limited by system resources of course), which means he can add as many levels of children he wants without altering his code. If you follow your method, you will have to write a new for loop for every new level of children (grandchildren, great grandchildren, great great, etc).
I know only a marginal amount about byte code and assembly, but if I took a guess, I would say Andeeee’s code might be slightly faster. But that is only a guess, nothing more. Hope this cleared up your concerns.