By Dubi Duboni


2018-11-08 22:55:54 8 Comments

using UnityEngine;
using System.Collections;
using UnityEditor;
using UnityEngine.SceneManagement;
using System;
using System.Collections.Generic;

public class EditorGUIHierarchyViewExampleWindow : EditorWindow
{
    EditorGUIHierarchyView hierarchyView = new EditorGUIHierarchyView();

    [MenuItem("Window/EditorGUIHierarchy - Show Scene View")]
    static void Init()
    {
        EditorGUIHierarchyViewExampleWindow window = (EditorGUIHierarchyViewExampleWindow)GetWindow(typeof(EditorGUIHierarchyViewExampleWindow));
        window.Show();
    }

    struct SceneGameObjectComparer : IComparer<GameObject>
    {

        public int Compare(GameObject go1, GameObject go2)
        {
            return go1.transform.GetSiblingIndex().CompareTo(go2.transform.GetSiblingIndex());
        }
    }

    void OnEnable()
    {
        this.titleContent = new GUIContent("Scene View");
    }

    public void OnGUI()
    {

        var gameObjects = SceneManager.GetActiveScene().GetRootGameObjects();
        Array.Sort(gameObjects, new SceneGameObjectComparer());

        hierarchyView.BeginHierarchyView();
        foreach (GameObject gameObject in gameObjects)
        {
            gameObject.transform.GetSiblingIndex();
            DrawGameObject(gameObject);
        }
        hierarchyView.EndHierarchyView();

        Repaint();
    }

    void DrawGameObject(GameObject go)
    {
        if (go.transform.childCount > 0)
        {

            if (go.activeInHierarchy)
            {
                hierarchyView.BeginNode(go.name);
            }
            else
            {
                hierarchyView.BeginNode(go.name, Color.gray, Color.gray);
            }

            foreach (Transform child in go.transform)
            {
                DrawGameObject(child.gameObject);
            }
            hierarchyView.EndNode();
        }
        else
        {

            if (go.activeInHierarchy)
            {
                hierarchyView.Node(go.name);
            }
            else
            {
                hierarchyView.Node(go.name, Color.gray, Color.gray);
            }

        }
    }
}

This is the EditorGUIHierarchyView class:

using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Collections.Generic;
using System.Text;

public class EditorGUIHierarchyView
{
    List<string> OpenIds = new List<string>();
    List<string> SelectedIds = new List<string>();

    GUIStyle foldoutChildessStyle;
    GUIStyle selectedAreaStyle;
    GUIStyle selectedLabel;
    GUIStyle selectedFoldout;
    GUIStyle normalFoldout;

    Color defaultSelectedTextColorDarkSkin = Color.white;
    Color defaultUnselectedTextColorDarkSkin = new Color(0.705f, 0.705f, 0.705f);
    Color defaultSelectedTextColorWhiteSkin = Color.white;
    Color defaultUnselectedTextColorWhiteSkin = Color.black;

    Color defaultSelectedTextColor;
    Color defaultUnselectedTextColor;
    bool defaultTextColorsSet;

    Vector2 scrollPosition;
    string previousNodeID;
    bool isCurrentNodeVisible = true;
    string lastVisibleNodeID;
    List<string> nodeIDBreadcrumb = new List<string>();

    public void BeginHierarchyView()
    {

        isCurrentNodeVisible = true;
        lastVisibleNodeID = null;
        nodeIDBreadcrumb.Clear();
        EditorGUI.indentLevel = 0;

        if (foldoutChildessStyle == null)
            CreateStyles();

        EditorGUILayout.BeginVertical();
        scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);

        previousNodeID = null;
    }

    void CreateStyles()
    {
        foldoutChildessStyle = new GUIStyle(EditorStyles.label);
        var padding = foldoutChildessStyle.padding;
        padding.left = 16;
        foldoutChildessStyle.padding = padding;


        selectedAreaStyle = new GUIStyle(GUIStyle.none);
        selectedAreaStyle.normal.background = MakeTex(1, 1, new Color(0.24f, 0.49f, 0.91f));
        selectedAreaStyle.active.background = selectedAreaStyle.normal.background;

        selectedLabel = new GUIStyle(foldoutChildessStyle);
        selectedLabel.normal.textColor = Color.white;

        selectedFoldout = new GUIStyle(EditorStyles.foldout);
        selectedFoldout.normal.textColor = Color.white;
        selectedFoldout.active.textColor = Color.white;
        selectedFoldout.focused.textColor = Color.white;
        selectedFoldout.onNormal.textColor = Color.white;
        selectedFoldout.onActive.textColor = Color.white;
        selectedFoldout.onFocused.textColor = Color.white;

        normalFoldout = new GUIStyle(EditorStyles.foldout);
        normalFoldout.active = normalFoldout.normal;
        normalFoldout.focused = normalFoldout.normal;
        normalFoldout.onActive = normalFoldout.onNormal;
        normalFoldout.onFocused = normalFoldout.onNormal;

        SetDefaultTextColors();
    }

    void SetDefaultTextColors()
    {
        if (defaultTextColorsSet)
            return;

        if (EditorGUIUtility.isProSkin)
        {
            defaultSelectedTextColor = defaultSelectedTextColorDarkSkin;
            defaultUnselectedTextColor = defaultUnselectedTextColorDarkSkin;
        }
        else
        {
            defaultSelectedTextColor = defaultSelectedTextColorWhiteSkin;
            defaultUnselectedTextColor = defaultUnselectedTextColorWhiteSkin;
        }

        defaultTextColorsSet = true;
    }

    private Texture2D MakeTex(int width, int height, Color col)
    {
        Color[] pix = new Color[width * height];

        for (int i = 0; i < pix.Length; i++)
            pix[i] = col;

        Texture2D result = new Texture2D(width, height);
        result.SetPixels(pix);
        result.Apply();

        return result;
    }


    bool IsOpened(string id)
    {
        return OpenIds.Contains(id);
    }

    void Open(string id)
    {
        if (!IsOpened(id))
        {
            OpenIds.Add(id);
        }
    }

    void Close(string id)
    {
        if (IsOpened(id))
        {
            OpenIds.Remove(id);
        }
    }

    void AddToSelection(string id)
    {
        SelectedIds.Add(id);
    }

    bool IsSelected(string id)
    {
        return SelectedIds.Contains(id);
    }

    void RemoveFromSelection(string id)
    {
        SelectedIds.Remove(id);
    }

    void SetSelected(string id)
    {
        SelectedIds.Clear();
        SelectedIds.Add(id);
        GUI.FocusControl(id);
    }

    //Returns true if this node is selected
    public bool BeginNode(string label)
    {
        return Node(label, true, defaultUnselectedTextColor, defaultSelectedTextColor);
    }

    //Returns true if this node is selected
    public bool BeginNode(string label, Color unselectedTextColor, Color selectedTextColor)
    {
        return Node(label, true, unselectedTextColor, selectedTextColor);
    }


    //Returns true if this node is selected
    public bool Node(string label, Color unselectedTextColor, Color selectedTextColor)
    {

        return Node(label, false, unselectedTextColor, selectedTextColor);
    }

    //Returns true if this node is selected
    public bool Node(string label)
    {

        return Node(label, false, defaultUnselectedTextColor, defaultSelectedTextColor);
    }

    bool Node(string label, bool isParent, Color unselectedTextColor, Color selectedTextColor)
    {

        var id = GetIDForLabel(label);

        if (isParent)
        {
            nodeIDBreadcrumb.Add(id);
        }

        if (!isCurrentNodeVisible)
            return false;


        bool wasOpened = IsOpened(id);
        bool isSelected = IsSelected(id);
        bool touchedInside = DrawNodeTouchableArea(id);
        GUI.SetNextControlName(id);
        bool opened = false;

        if (isParent)
        {
            GUIStyle styleToUse = isSelected ? selectedFoldout : normalFoldout;
            Color colorToUse = isSelected ? selectedTextColor : unselectedTextColor;
            styleToUse.normal.textColor = colorToUse;
            styleToUse.onNormal.textColor = colorToUse;
            styleToUse.active.textColor = colorToUse;
            styleToUse.onActive.textColor = colorToUse;
            styleToUse.focused.textColor = colorToUse;
            styleToUse.onFocused.textColor = colorToUse;

            opened = EditorGUILayout.Foldout(wasOpened, label, styleToUse);

            if (isSelected && Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.RightArrow)
            {
                opened = true;
            }
            else if (isSelected && Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.LeftArrow)
            {
                opened = false;
            }
        }
        else
        {
            GUIStyle styleToUse = isSelected ? selectedLabel : foldoutChildessStyle;
            Color colorToUse = isSelected ? selectedTextColor : unselectedTextColor;

            styleToUse.normal.textColor = colorToUse;
            styleToUse.active.textColor = colorToUse;

            EditorGUILayout.LabelField(label, styleToUse);
        }

        EditorGUILayout.EndHorizontal();

        bool useCurrentEvent = false;

        if (wasOpened != opened)
        {
            useCurrentEvent = true;
            if (opened)
                Open(id);
            else
                Close(id);
        }
        else if (touchedInside)
        {
            useCurrentEvent = true;
            if (Event.current.command)
            {
                if (IsSelected(id))
                {
                    RemoveFromSelection(id);
                }
                else
                {
                    AddToSelection(id);
                }
            }
            else
                SetSelected(id);
        }

        HandleKeyboardCycle(previousNodeID, id);

        previousNodeID = id;

        if (useCurrentEvent)
        {
            Event.current.Use();
        }


        if (isParent && !opened)
        {
            isCurrentNodeVisible = false;
            lastVisibleNodeID = id;
        }

        if (isParent)
        {
            EditorGUI.indentLevel++;
        }

        return IsSelected(id);
    }

    public void EndNode()
    {
        string endedNodeId = nodeIDBreadcrumb[nodeIDBreadcrumb.Count - 1];
        if (endedNodeId == lastVisibleNodeID)
        {
            isCurrentNodeVisible = true;
            lastVisibleNodeID = null;
        }
        nodeIDBreadcrumb.RemoveAt(nodeIDBreadcrumb.Count - 1);

        if (isCurrentNodeVisible)
            EditorGUI.indentLevel--;
    }


    string GetIDForLabel(string label)
    {
        StringBuilder sb = new StringBuilder();
        foreach (string id in nodeIDBreadcrumb)
        {
            sb.Append(id);
            sb.Append("_");
        }

        sb.Append(label);
        return sb.ToString();
    }

    void HandleKeyboardCycle(string previousNodeID, string currentNodeID)
    {
        if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.DownArrow)
        {
            if (IsSelected(previousNodeID))
            {
                Event.current.Use();
                SetSelected(currentNodeID);
            }
        }
        else if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.UpArrow)
        {
            if (IsSelected(currentNodeID))
            {
                Event.current.Use();
                SetSelected(previousNodeID);
            }
        }
    }

    bool DrawNodeTouchableArea(string id)
    {
        var area = EditorGUILayout.BeginHorizontal(IsSelected(id) ? selectedAreaStyle : GUIStyle.none, GUILayout.ExpandWidth(true));
        Event currentEvent = Event.current;
        bool touchedInside = false;
        if (currentEvent.type == EventType.MouseUp)
        {
            Vector2 mousePosition = currentEvent.mousePosition;
            if (area.Contains(mousePosition))
            {
                touchedInside = true;
            }
        }

        return touchedInside;
    }


    public void EndHierarchyView()
    {

        if (nodeIDBreadcrumb.Count > 0)
        {
            Debug.LogError("Called EndHierarchyView with nodes still opened. Please ensure that you have a EndNode() for every BeginNode()");
        }

        EditorGUILayout.EndVertical();
        EditorGUILayout.EndScrollView();
    }
}

The script show all the hierarchy scene objects in the editorwindow. I want then in the editorwindow to manipulate and make changes to the objects.

The problem is that scrolling up down between the objects and childs is very slow not like in the original hierarchy window that is much faster.

The main goal is to show the hierarchy scene gameobjects in the editorwindow and then work on the objects in the editor window.

1 comments

@Levon Ravel 2018-11-08 23:18:14

From what I can see your foreach loop is constantly getting a sibling index every iteration along with all the gameObjects in the scene. You should initially grab the gameobjects store them into a class with variables and then add that to a Collection. Iterate that collection and just pull the values.

EditorGUIHierarchyView hierarchyView = new EditorGUIHierarchyView();

private static GameObject[] _sceneObjects;

[MenuItem("Window/EditorGUIHierarchy - Show Scene View")]
static void Init()
{
    EditorGUIHierarchyViewExampleWindow window = (EditorGUIHierarchyViewExampleWindow)GetWindow(typeof(EditorGUIHierarchyViewExampleWindow));
    _sceneObjects = SceneManager.GetActiveScene().GetRootGameObjects();
    Array.Sort(_sceneObjects, new SceneGameObjectComparer());
    window.Show();
}

struct SceneGameObjectComparer : IComparer<GameObject>
{
    public int Compare(GameObject go1, GameObject go2)
    {
        return go1.transform.GetSiblingIndex().CompareTo(go2.transform.GetSiblingIndex());
    }
}

void OnEnable()
{
    this.titleContent = new GUIContent("Scene View");
}

public void OnGUI()
{
   hierarchyView.BeginHierarchyView();
    foreach (GameObject gameObject in _sceneObjects)
    {
        gameObject.transform.GetSiblingIndex();
        DrawGameObject(gameObject);
    }
    hierarchyView.EndHierarchyView();

    Repaint();
}

void DrawGameObject(GameObject go)
{
    if (go.transform.childCount > 0)
    {

        if (go.activeInHierarchy)
        {
            hierarchyView.BeginNode(go.name);
        }
        else
        {
            hierarchyView.BeginNode(go.name, Color.gray, Color.gray);
        }

        foreach (Transform child in go.transform)
        {
            DrawGameObject(child.gameObject);
        }
        hierarchyView.EndNode();
    }
    else
    {

        if (go.activeInHierarchy)
        {
            hierarchyView.Node(go.name);
        }
        else
        {
            hierarchyView.Node(go.name, Color.gray, Color.gray);
        }

    }
}

@Dubi Duboni 2018-11-08 23:28:22

Maybe some more faster. If I did it right. I just copied your answer.

@Dubi Duboni 2018-11-08 23:28:49

How the original hierarchy scene window is working ? How it's working so fast ?

@Levon Ravel 2018-11-08 23:32:06

OnGUI is like the Update method in Unity3d, So it constantly runs calling the whole hierarchy structure everytime in that loop is a huge performance hit. I am updating the code to reflect how I might do it.

@Levon Ravel 2018-11-08 23:36:34

From what I have posted above you will need to make sure that when the hierarchy changes you update the _sceneObjects.

Related Questions

Sponsored Content

16 Answered Questions

[SOLVED] Is there a way to check if a file is in use?

0 Answered Questions

25 Answered Questions

[SOLVED] What is the best way to iterate over a dictionary?

  • 2008-09-26 18:20:06
  • Jake Stewart
  • 1227084 View
  • 2065 Score
  • 25 Answer
  • Tags:   c# dictionary loops

3 Answered Questions

1 Answered Questions

C# Network Configuration Change not working

0 Answered Questions

Gridview footer Textbox javascript call not working after two insert

20 Answered Questions

[SOLVED] Best way to parse command line arguments in C#?

2 Answered Questions

[SOLVED] c# tooltip backcolor and forecolor are not changing with using method or other ways

  • 2012-10-04 08:29:40
  • Colin Henricks
  • 3789 View
  • 3 Score
  • 2 Answer
  • Tags:   c# tooltip

1 Answered Questions

[SOLVED] Custom Validation Attribute is not called ASP.NET MVC

1 Answered Questions

[SOLVED] Is it ok to use the same interface definition but provide different behaviour?

  • 2009-03-02 23:21:32
  • Kepboy
  • 106 View
  • 0 Score
  • 1 Answer
  • Tags:   c# oop

Sponsored Content