A bit of organization in my Hierarchy

I just wanted to show how I organized my hierarchy objects to keep things easier for myself, in case anyone wants to copy this setup or something similar.

image

Notes:

  • Each parent title object, and each “------” object is just an empty gameobject with its transform reset
  • Having this parent objects allows me to minimize each category as needed, to be able to find stuff better
  • I modified the scripts so that each GridDebugObject is instanciated as child of GridDebugObjects, same with the GridSystemVisualSingles when you start the game. To also keep that objects organized.

Thanks!

3 Likes

Yup that’s a great way to keep organized. I really wish Unity had proper folder-like objects instead of using a regular game object.
One thing, do make sure those container game objects are on 0,0,0 otherwise you might come across some weird bugs if any of the child objects do anything with global position.

1 Like

I have two things in my games that cannot be child of another gameobject
The QuantumConsole, and the NetworkManager. Because they have a dontDestroyOnLoad, I think thats whats causing problems.

Do you know if there is any way I could get that objects to be a child of my “folder like” object?

Another question:
image

Do you know if there is any way for this icons to show in the Hierarchy? Because I apply them, but they do not show.

Thanks!

There is no way (that I am currently aware of) to have DDoL objects not be the root object. I believe this is because Unity actually copies them to another scene that is loaded additively. When you run your game, you will see that it has moved to a ‘scene’ called DontDestroyOnLoad.

Not by default. I will share some code here, but take no responsibility for it :grin:

This first bit comes from Thomas Brush (with minor tweaks from me).
Make a folder called Editor and make a script inside it called HierarchySectionHeader.cs. Put this in there

/// <summary>
/// Courtesy of Thomas Brush (with minor tweaks)
/// </summary>
[InitializeOnLoad]
public static class HierarchySectionHeader
{
    static Color HEADER_BACKGROUND_COLOR = new Color(80f / 255f, 80f / 255f, 80f / 255f);

    static HierarchySectionHeader() => EditorApplication.hierarchyWindowItemOnGUI += HierarchyWindowItemOnGUI;

    static void HierarchyWindowItemOnGUI(int instanceID, Rect selectionRect)
    {
        var gameObject = EditorUtility.InstanceIDToObject(instanceID) as GameObject;
        if (gameObject == null) return;
        if (!gameObject.name.StartsWith("//")) return;

        EditorGUI.DrawRect(selectionRect, HEADER_BACKGROUND_COLOR);
        EditorGUI.DropShadowLabel(selectionRect, gameObject.name.Substring(2));
    }
}

Now when you add an empty, you can start its name with // to turn it into a header


Building on this, I have messed around a bit with it. It’s not pretty, but you could add the selected gizmo icon by just getting it and adding it to the header

static void HierarchyWindowItemOnGUI(int instanceID, Rect selectionRect)
{
    var gameObject = EditorUtility.InstanceIDToObject(instanceID) as GameObject;
    if (gameObject == null) return;
    if (!gameObject.name.StartsWith("//")) return;

    EditorGUI.DrawRect(selectionRect, HEADER_BACKGROUND_COLOR);
    EditorGUI.DropShadowLabel(selectionRect, gameObject.name.Substring(2));

    // Try to get the icon for this object and display it if it exists
    var icon = EditorGUIUtility.GetIconForObject(gameObject);
    if (icon is null) return;
    var height = Mathf.Min(icon.height, selectionRect.height);
    var imgRect = new Rect(selectionRect.x, selectionRect.y, height * 2f, height);

    var guiColor = GUI.color;
    try
    {
        GUI.color = Color.clear;
        EditorGUI.DrawTextureTransparent(imgRect, icon, ScaleMode.ScaleToFit, (float)icon.width / (float)icon.height);
    }
    finally
    {
        GUI.color = guiColor;
    }
}

Some of that code is a bit of messing around trying to get the icons to look good in the hierarchy


I don’t personally like the icons very much. What I have done previously was to add some sort of ‘tag’ that I could use to color the headers

static void HierarchyWindowItemOnGUI(int instanceID, Rect selectionRect)
{
    var gameObject = EditorUtility.InstanceIDToObject(instanceID) as GameObject;
    if (gameObject == null) return;
    if (!gameObject.name.StartsWith("//")) return;

    var objectName = gameObject.name[2..];

    var color = HEADER_BACKGROUND_COLOR;
    if (gameObject.name[2..].StartsWith("[c=#"))
    {
        var hexColor = gameObject.name[5..12];
        if (ColorUtility.TryParseHtmlString(hexColor, out Color parsed))
        {
            color = parsed;
            objectName = gameObject.name[13..];
        }
    }

    EditorGUI.DrawRect(selectionRect, color);
    EditorGUI.DropShadowLabel(selectionRect, objectName);
}

This lets me specify the color I want the ‘header’ to be by adding [c=#rrggbb] after the // and before the actual name


It is important to note what Hugo said, though: Make sure these ‘headers’ are at (0,0,0) or the children might behave funny.

These scripts (except the first one from Thomas) were slammed together very quickly and may very well break your editor, so use them at your own risk. There are things that could be better (like the way I parsed the color from the name) but it is what it is. It’s not like I’m going to get fired or anything…

Edit I have gone much further and created a MonoBehaviour with color properties and stuff that define the color for the header and removes the need for weird naming conventions, and had this script read those instead, but I haven’t actually used it in a project.


Late Edit
I cleaned up the color tag code a bit (if you want to consider regular expressions as ‘cleanup’). Also removed the ‘c=’ bit from the color tag because it’s not really necessary

static Regex _regex = new Regex("^(?<tag>//)?(?:\\[(?<color>#[0-9A-F]{6}){1}\\])?(?<title>.*)$", RegexOptions.IgnoreCase);

static void HierarchyWindowItemOnGUI(int instanceID, Rect selectionRect)
{
    var gameObject = EditorUtility.InstanceIDToObject(instanceID) as GameObject;
    if (gameObject == null) return;

    var match = _regex.Match(gameObject.name);

    // Did we match the regex? If not, ignore and treat like any other object
    if (!match.Success) return;
    // Does it have our tag? If not, ignore and treat like any other object
    if (string.IsNullOrWhiteSpace(match.Groups["tag"].Value)) return;

    // We have a 'header'. Try to parse color, if any
    var color = HEADER_BACKGROUND_COLOR;
    if (ColorUtility.TryParseHtmlString(match.Groups["color"].Value, out Color parsed))
        color = parsed;

    // Grab the title
    var title = match.Groups["title"].Value;

    // Draw the 'header'
    EditorGUI.DrawRect(selectionRect, color);
    EditorGUI.DropShadowLabel(selectionRect, title);
}


With some complimentary colors :sunglasses:

3 Likes

Privacy & Terms