Search Unity

Contribution Hierarchy Labels

Discussion in 'Open Projects' started by IsaiahKelly, Mar 8, 2021.

  1. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    I've noticed that a few official Unity projects like to use dummy GameObjects in the scene to separate content in the hierarchy view.

    hacky_way.png

    This works okay but feels really hacky and less than ideal, so I decided to try and see if I could improve upon the concept, and I came up with something I call "hierarchy labels".

    better_way.png

    The whole system consist of only four small scripts:
    • Hierarchy Label: A MonoBehaviour you attach to the object that will display the label.
    • Hierarchy Label Data: An optional ScriptableObject to store shared label data.
    • Hierarchy Label Editor: A custom editor to control how fields are shown when using shared label data.
    • Hierarchy Label Drawer: The class which draws all the labels in the hierarchy window.

    I could have called this a "separator" instead of a "label" but I realized you could also use them on hierarchy "folders" too. So it is not just useful for separating things.

    I think the benefits of this tiny addition are well worth it, but I want to get some feedback on this feature before doing any pull request, and also discuss a few possible extensions to it. At the moment these are the label settings:

    label_inspector.png

    One possible addition here could be to use ScriptableObjects to store configuration data instead of the MonoBehaviour itself. Which would be useful for adding the same kind of label to multiple scenes and keeping them all consistent. We could maybe even add localization to labels! But this will make the code more complex and it already works perfectly fine as is. So maybe we should just start with this?

    Update: Feature pull request can be found here.
     
    Last edited: Jun 30, 2021
  2. Harsh-NJ

    Harsh-NJ

    Joined:
    May 1, 2020
    Posts:
    315
    Good Idea, improves readability.
     
  3. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    Thanks @Harsh-099.

    I went ahead and added an optional ScriptableObject data holder for sharing label settings, since it was pretty easy to implement.

    label_data.png

    I will probably just go ahead and create a pull request for this now because these forums do not appear to be as active as I originally thought :(. I hope the project is doing alright and interest has not dropped like a rock!
     
    Schodemeiss likes this.
  4. Harsh-NJ

    Harsh-NJ

    Joined:
    May 1, 2020
    Posts:
    315
    No, the interest for this project is not a rock. Just @cirocontinisio and @Amel-Unity are busy with other stuff and they are coming and seeing the threads here once in a while.

    And you didn't called them at least one time (using @)
    Its a good contribution for improving readability. You should open a PR, as I and others are waiting to see how you did this.
     
  5. Schodemeiss

    Schodemeiss

    Joined:
    Jan 4, 2018
    Posts:
    43
    I've not seen the code for this yet, but another interesting thing you might be able to enforce from this Label Monobehaviour is setting the Tag to "EditorOnly". This will ensure those objects are not in the final build (as they're not needed anyway).

    Perhaps another bool called "EditorOnly", then if that's true, in OnValidate or OnReset (or both) you could set the tag; that way it should stay set the whole time that Label is applied.
     
  6. Harsh-NJ

    Harsh-NJ

    Joined:
    May 1, 2020
    Posts:
    315
    Yes, we mark them EditorOnly while creating them, but if any misses by mistake, this will take care of that.
     
    Schodemeiss likes this.
  7. Schodemeiss

    Schodemeiss

    Joined:
    Jan 4, 2018
    Posts:
    43
    Actually, reflecting on this a little, a more "Re-useable Components Approach" to this would make it a separate component entirely. Then on top of your HierchacyLabel class, use the RequireComponent attribute.
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. namespace UOP1
    4. {
    5.    /// <summary>Ensures any <see cref="GameObject" /> this <see cref="MonoBehaviour" /> is attached to has it's Tag set to <see cref="EDITOR_ONLY_TAG" />.</summary>
    6.    public sealed class EnsureEditorOnlyTag : MonoBehaviour
    7.    {
    8. #if UNITY_EDITOR
    9.       /// <summary>The "EditorOnly" tag string.</summary>
    10.       private const string EDITOR_ONLY_TAG = "EditorOnly";
    11.  
    12.       /// <summary>Reset is called when the user hits the Reset button in the Inspector's context menu or when adding the component the first time.</summary>
    13.       private void Reset()
    14.       {
    15.          EnsureEditorOnlyTagSet();
    16.       }
    17.  
    18.  
    19.       /// <summary>This function is called when the script is loaded or a value is changed in the Inspector.</summary>
    20.       private void OnValidate()
    21.       {
    22.          EnsureEditorOnlyTagSet();
    23.       }
    24.  
    25.       /// <summary>Sets the Tag to <see cref="EDITOR_ONLY_TAG" /> on the <see cref="GameObject" /> this <see cref="MonoBehaviour" /> is attached to.</summary>
    26.       private void EnsureEditorOnlyTagSet()
    27.       {
    28.          if (!CompareTag(EDITOR_ONLY_TAG)) gameObject.tag = EDITOR_ONLY_TAG;
    29.       }
    30. #endif
    31.    }
    32. }
    33.  
    Then the attribute on your class would look something like:
    Code (CSharp):
    1. [RequireComponent(typeof(EnsureEditorOnlyTag))]
    If you think this is a good idea you're welcome to include the above code in your PullRequest when you make it, or I can make a commit on your repo with this addition. Totally up to you!

    Note: I didn't include Awake() in the script as OnValidate() get's called whenever you open a scene, so it'll always set itself, but you could add Awake() if you really felt the need, or any number of the MonoBehaviour callbacks to make absolutely certain the tag is set, but that's likely overkill.

    I also didn't wrap the whole class in a #if UNITY_EDITOR define. This is because of the admittedly unlikely scenario of someone changing the tag on the object manually in the inspector, then immediately starting a build. If you wrapped the whole class, the compile will fail due to the gameobject having a reference to the now missing MonoBehaviour. Again, not likely, but there's no point in breaking the whole build due to that mistake and if Code Stripping is enabled on the project, Unity should whip the whole thing away anyway.
     
    Last edited: Mar 30, 2021
  8. Harsh-NJ

    Harsh-NJ

    Joined:
    May 1, 2020
    Posts:
    315
    I don't think that it is favourable to attach two scripts to a object that is EditorOnly. But @IsaiahKelly could combine your code in his script. This way we will end up with only one script, improving readability and ensuring EditorOnly at the same time.

    And @IsaiahKelly, I really want to know that how you did this.
     
  9. Schodemeiss

    Schodemeiss

    Joined:
    Jan 4, 2018
    Posts:
    43
    Absolutely, they'd be very welcome to do that. I just posted some code I quickly knocked together in the case it'd be useful to someone. I'd be curious to understand you're reason behind not wanting to create a separate reusable MonoBehaviour for just that task? Especially given the ease of which it can be applied with the RequireComponent attribute?

    You mention readability, but I'd posit moving more logic into the HierarchyLabels code might just make that component more verbose, thus making that one less readable? But it's totally up to @IsaiahKelly, I was just interested to understand your reasoning.

    Me too :), I've not dabbled much in editing the Hierachy view much. Intrigued to see what magic they've weaved!
     
  10. Harsh-NJ

    Harsh-NJ

    Joined:
    May 1, 2020
    Posts:
    315
    A separate component could be added, but existing script could also handle this task easily. And for the readability, I was taking about the readability in the hierarchy. But it's up to @IsaiahKelly to include this code in his script or make a separate one.

    Yes, the magic.
     
  11. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    Hey, sorry for my absence.

    I actually already did that, but was wondering if I should not, since you could place the label on an object that you do not want to remove from builds. Such as a parent object meant to hold children. Thanks for all the suggestions but I also agree with @Harsh-099 that making a separate script for editor only enforcement is probably unnecessary.

    If I remember correctly, right now I only mark the label object "editor only" when creating a new one from the context menu. That way it will not enforce "editor only" when adding the label component to an existing object, in case you don't want that. But I could also just set "editor only" tag in the reset callback. That way you can change it after adding the script and it will not change back, like with OnValidate.
     
  12. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    Okay, so I decided to try and keep this as simple as possible and just add the "EditorOnly" tag on reset, and not worry about child objects and such.

    Ideally, we should check to ensure no child objects are attached to the label because they will be removed from builds. And this could create hard to track bugs if someone accidentally added some child to it. However, I couldn't find an elegant way to check for children and/or prevent them from being added to the label object. But it's pretty unlikely to happen anyway so it's probably fine.

    @Harsh-099 @Schodemeiss I have finally submitted a pull request for this feature, so you can checkout the code. Sorry it took me so long! Got a bit distracted by other things.
     
  13. cirocontinisio

    cirocontinisio

    Joined:
    Jun 20, 2016
    Posts:
    884
    It's a cool idea!

    I share the doubts about making it EditorOnly... also, what happens if you do parent something on one of those objects? Do you see the little triangle? Do you see the children? Can you remove them?

    The other thing I'd say is that personally I'd restrict the colours to a couple, or just one shade of gray. Coloured labels are very noisy and will end up being annoying more than useful, since they area always fighting for your attention.
    Or maybe we can use very muted colours, like pastel shades or such (?)
     
  14. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    Thanks for the feedback @cirocontinisio.

    I have thought of a few possible ways to address this:
    • Check for children and only set "EditorOnly" if none exist.
    • Display a warning message on the component in the editor that this label object and any children will be removed from builds.
    • Manually destroy the label object on awake in builds, after orphaning any children so they do not get destroy. This is the approach I've already used for hierarchy "folders" in the past.
    • Just don't set "EditorOnly" at all?
    Yes to all. The custom label only draws over the object icon and text display area. So everything else functions normally and you can still see that little "expand children" icon, etc.

    I did however add logic to lock the transform component to prevent editing, to indicate this object isn't meant for that. But I have now changed it to just hide the transform component completely, which makes it look a lot tidier overall. Here is a screenshot of my latest tweaks:

    label-example.png

    These visual changes have not yet been committed to the PR, so let me know if you think these would be an improvement or not.

    As for label colors; that is a good point I have not thought about. But I've left color choices entirely up to whoever adds them to a scene. So you're free to choose whatever color schemes you think are best.

    I could also expand upon the "shared data" concept to help enforce certain rules. For example, I could require one shared data ScriptableObject for all label data, and create a custom editor for the label component that would have a popup selection list for each label type, etc. But I am trying to avoid making this too complex. However, it is an editor only feature after all. So there is no real fear of conflicts or issues with other parts of the project.
     
    Last edited: Jun 29, 2021
  15. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    So I decided to actually go back to the way it functioned at first, where I only set the tag to "EditorOnly" on new label objects. Since this would not create any more issues than doing so manually anyway. The component itself now never touches tags or hides the transform, to keep everything simple and straightforward.

    I also improved the custom editor to now actually swap the local instance fields for the shared ScriptableObject ones seamlessly, instead of just hiding the instance fields.

    label_modes.gif
     
    Last edited: Jul 1, 2021
  16. cirocontinisio

    cirocontinisio

    Joined:
    Jun 20, 2016
    Posts:
    884
    In general I think it's good advice to have more root objects than fewer (for reasons explained here) and that's why I use the hierarchy dividers as dividers and not as folders.
    So for this reason I wouldn't parent anything under them.
     
  17. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    Yeah, I am familiar with the performance issues related to having many parented objects, and how you want to keep the hierarchy as flat as possible. That is why the fake "folder" objects would remove themselves on awake. Now using the "EditorOnly" tag would be much better, because then no "folder" object would end up in a build. But in my tests that tag also removed all children from builds, even when the children do not have the tag! So it would be nice if Unity gave us more granular control over the "EditorOnly" tag on GameObjects ;)
     
  18. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    In any case, I have now made the label script function just like any other component and stopped trying to enforce any special rules on how it can or cannot be used. This is because all my efforts to do so only seemed to needlessly complicate an otherwise elegantly simple system.
     
    cirocontinisio likes this.
  19. cirocontinisio

    cirocontinisio

    Joined:
    Jun 20, 2016
    Posts:
    884
    Funnily enough, I also feel they are two conflicting forces:
    - You need dividers when you have a lot of root objects, because the list is long and you don't want to read each name, so dividers visually help with breaking the long list into sections.
    - Folders/few root objects instead make the list short, at which point you don't need dividers anymore. So if that is what we want, we could literally make a script that unparents all children and destroys the GO on Start. But things can get pretty messy if somebody decides to use these scripts in nested GameObjects...

    That's why I favour a simple divider object, no hassle, and no possibility of introducing bugs due to the unparenting. And the tag means it doesn't even end into the build (minor thing). Happy to go with the custom visualisation, again, as long as we don't use screaming fluo colours.
     
  20. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    @cirocontinisio You make a great observation I had not really noticed: "divider" objects are basically used as a substitute for "folder" objects in Unity, because of the transform hierarchy performance issues. And this helps explain why I've seen them used in multiple official Unity projects.

    Yeah, we could create "folder" objects that automatically remove themselves instead, and even enforce how they can be used. But there is still a huge cost associated with removing them at runtime, and the "EditorOnly" tag will not help us here. So it's still worse than just using "dividers".

    Overall, I think the need to create these kinds of dummy objects in the first place really shows a big flaw in the current hierarchy editor tooling. Fortunately, it looks like the new selection groups package might be the answer!


    Ha! I would just like to stress that the colors I used in these screenshots were for demonstration purposes only! So I am not trying to suggest we need to have psychedelic labels. I should, however, probably change the default label color to something other than cyan. :D
     
    cirocontinisio likes this.
  21. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    I also think the main reason I used such vibrant colors in the screenshots was to demonstrate just how much easier they are to distinguish from regular objects, but I probably went a little overboard! That and the fact it's not exactly easy to choose a color that works for both dark and light editor themes.

    This last issue has also made me think that maybe I should add some logic to set or adjust the label color, based on current editor theme...
     
    Last edited: Jul 3, 2021
  22. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    Okay, so I kind of fell down the rabbit hole while trying to see how I might improve this feature, and I discovered a few interesting facts.
    1. It appears that setting the "EditorOnly" tag on a GameObject just tells Unity to set the GameObject's hide flags to HideFlags.DontSaveInBuild, since both seem to do the same exact thing. However, you can also manually set hide flags on select components on a game object, which gives you more granular control.
    2. Setting HideFlags.NotEditable on the GameObject itself disables adding and removing child transforms and components, just like we want for labels. Unfortunately, it also disables editing of the label script itself.
    3. Turns out there are also other advanced ways to remove or modify scenes before or after building the player.
    This last fact helped me discover a way to remove these "folder" objects from each scene, right before building the player. Making them a viable option now, since this completely eliminates the performance costs of having to remove them at runtime on awake.

    So I have now created a new feature I'm calling "Scene Folders" that currently look like this:

    Scene Folders
    scene_folders.gif

    These feel so much cleaner and tidier than using labels/dividers/separators. I could also just include this feature alongside hierarchy labels, but this pretty much eliminates the need for the latter altogether. So it would be somewhat redundant.

    Here is also a look at my latest experiments with the label visuals, for comparison.

    Hierarchy Labels
    scene_labels.png

    @cirocontinisio Both features use about the same amount of code, so do you have any preference here with which one you think we should use for this project?
     
    Last edited: Jul 4, 2021
  23. dylannorth

    dylannorth

    Joined:
    Dec 26, 2015
    Posts:
    9
    Hi Isaiah, I just happened upon this thread and I really like how the labels/folders ended up looking in your latest iteration. I checked the PR and it seems it wasn't updated since March, so I was wondering would you be able to share your progress from July on this?