Search Unity

Unity Overlays Developer Guide

Discussion in 'Editor Workflows' started by gabrielw_unity, Dec 8, 2020.

  1. gabrielw_unity

    gabrielw_unity

    Unity Technologies

    Joined:
    Feb 19, 2018
    Posts:
    882
    How to Build Your Own Overlays

    Hello! Here’s a quick-start guide to building your own Overlays with the new system. Please post questions and discussions here, we'll be happy to help out.

    For general Overlays info, and to download the public build, view the Overlays Public Preview thread.

    Please keep in mind, this is an early preview, subject to change.

    The Simplest Way

    1. Create a class that inherits from Overlay class

    class MyOverlay : Overlay
    {
    }


    2. Add the Overlay attribute and provide:
    • Which type of window it can appear in, use EditorWindow type if you want it to appear in any window that supports overlays
    • a unique id
    • A name, this will be displayed in its header and menu
    [Overlay(typeof(SceneView), k_Id, "My Awesome Overlay")]
    class MyOverlay : Overlay
    {
    const string k_Id = "my-custom-overlay";
    }


    3. Implement the CreatePanelContent method and have it return the content you wish to be displayed in the overlay. From this point refer to UI Toolkit docs to know how to build UI.

    public override VisualElement CreatePanelContent()
    {
    var myContent = new VisualElement { name = "my-content" };
    return myContent;
    }


    4. Add the icon attribute to specify which icon to use when collapsed, if no attribute is specified an icon with the 2 most significant letters of the overlay name will be generated for you

    Important Note: IconAttribute is not available in the current preview build, but will be public when released.

    [Overlay(typeof(SceneView), k_Id, "My Awesome Overlay")]
    [Icon("path/to/myIcon.png")]
    class MyOverlay : Overlay
    {
    const string k_Id = "my-custom-overlay";
    }


    Note: If your overlay does not appear, be aware that is the default behavior. Bring up the overlay menu by pressing the space bar in the Scene View (you can change this in the shortcut manager) you should see your overlay appear in the menu.

    Lifecycle

    Overlay visibility can be controlled through Overlay Menu, the 3 dot menu in the corner of the window or through code by directly changing the value of the displayed property. You may want to prevent the users from controlling the visibility and control it yourself ( from a toggle in a toolbar for example ) in this case be sure to set the property userControlledVisibility to false, in the Overlay constructor for example.

    Toolbars

    If implemented this way, you will notice that your overlay will collapse when inserted into a toolbar, you can change this behavior by implementing the ICreateHorizontalToolbar and the ICreateVerticalToolbar interfaces.

    [Overlay(typeof(SceneView), k_Id, "My Awesome Overlay")]
    class MyOverlay : Overlay, ICreateHorizontalToolbar, ICreateVerticalToolbar
    {
    const string k_Id = "my-custom-overlay";
    public override VisualElement CreatePanelContent()
    {
    var myContent = new VisualElement { name = "my-content" };
    return myContent;
    }
    public VisualElement CreateHorizontalToolbarContent()
    {
    var myContent = new VisualElement { name = "my-horizontal-content" };
    return myContent;
    }
    public VisualElement CreateVerticalToolbarContent()
    {
    var myContent = new VisualElement { name = "my-vertical-content" };
    return myContent;
    }
    }


    IMGUI

    If you are converting some existing UI code to use the overlay system, chances are it is written in IMGUI. We strongly advise to take this opportunity to convert your whole UI to UITK. In the case where this is not possible you can use the IMGUIOverlay class.

    [Overlay(typeof(SceneView),k_OverlayID,k_DisplayName)]
    internal class MyIMGUIOverlay : IMGUIOverlay
    {
    public const string k_OverlayID = "my-custom-overlay";
    const string k_DisplayName = "My Awesome Overlay";
    public override void OnGUI()
    {
    //Draw IMGUI stuff here
    }
    }


    Other Windows

    The overlay system is not restricted to the SceneView, any EditorWindow can opt in to have it enabled. All it needs to do is implement the ISupportsOverlays interface. Overlays targeting EditorWindow will automatically be available, otherwise Overlays need to target your EditorWindow type.
     
    Last edited: Dec 8, 2020
    Sab_Rango, Mauri, Shaderic and 5 others like this.
  2. Catsoft-Studios

    Catsoft-Studios

    Joined:
    Jan 15, 2011
    Posts:
    672
    That's awesome, waiting for the weekend to try this out. One question, if I may. That [Icon] Attribute, does it necessarily need a string path?

    Will there be a mechanism to inherit from the IconAttribute class and implement your own custom icon? Somthing like this:

    Code (CSharp):
    1. public MyCustomIconAttribute : IconAttribute
    2. {
    3.     public override Texture iconTexture => GenerateMyCustomTexture();
    4.  
    5.     private Texture GenerateMyCustomTexture()
    6.     {
    7.         // custom code that generates a Texture, or loads it from somewhere.
    8.     }
    9. }
    And use it like this

    Code (CSharp):
    1. [Overlay(typeof(SceneView), k_Id, "My Awesome Overlay")]
    2. [MyCustomIcon]
    3. class MyOverlay : Overlay
    4. {
    5.    const string k_Id = "my-custom-overlay";
    6. }
    The reason being that you don't always want to hard-code textures or maybe you want to generate them on the fly (based on whether Unity dark skin is on or off), among other reasons.

    Anyway, I'm super excited about this new Overlays feature!
     
  3. kaarrrllll

    kaarrrllll

    Unity Technologies

    Joined:
    Aug 24, 2017
    Posts:
    477
    The icon loading follows the same path that built-in icons do. It will search for icons matching a naming scheme to best match the current editor theme and screen resolution. Ex, you can provide MyIcon.png d_MyIcon.png MyIcon@2x.png d_MyIcon@2x.png (@2x being resolution, with the "d_" prefix to specific professional theme).
     
    Catsoft-Studios likes this.
  4. neil_devine

    neil_devine

    Unity Technologies

    Joined:
    Apr 8, 2018
    Posts:
    35
    Hi @Catsoft-Studios, @kaarrrllll beat me to it. At this point yes it does need a string path and unfortunately in this build the IconAttribute is internal, we will make sure to make it public for release. Interested to know what your other use cases would be for generating icons.
     
    Last edited: Dec 9, 2020
    Catsoft-Studios likes this.
  5. Catsoft-Studios

    Catsoft-Studios

    Joined:
    Jan 15, 2011
    Posts:
    672
    Sure! I guess it's a more personal reason, so I understand that's not something that would be widely used. But as an Asset Store developer, I try to have the least amount of textures inside each scripting package. The reason being that I don't want to pollute the user's project with textures he/she shouldn't be using (gizmos, Inspector icons, ...). Otherwise, when they click on any Texture field picker, they get hundreds of textures non-related to their game project.

    For this reason, I'm moving all icons to a hardcoded byte[] array that is reconstructed whenever it's needed using the Texture2D.LoadRawTextureData(...) method. This has the advantage of not getting in the way of the user's texture organisation or naming convention.

    Hope that makes sense, though as I said, as far as I know only Bolt and my (future) packages do it like this. I am more than happy to provide feedback if you have any questions to discuss :)
     
  6. gabrielw_unity

    gabrielw_unity

    Unity Technologies

    Joined:
    Feb 19, 2018
    Posts:
    882
    That's a very cool idea! How is it working out? Similar problems for us/others, of course. If this is a good solution, I can definitely see us wanting to ensure Overlays can work with it.
     
    awesomedata and Catsoft-Studios like this.
  7. Catsoft-Studios

    Catsoft-Studios

    Joined:
    Jan 15, 2011
    Posts:
    672
    Thanks! So far it's working fantastic! I've been using this for a couple of months and there aren't any drawbacks so far. I've attached a screenshot showing how it looks. I wouldn't want to hijack this thread's topic :p So before doing so, let me know if you want me to give the implementation details here or somewhere else.

    Screenshot 2020-12-10 at 18.46.28.png
     
    Last edited: Dec 10, 2020
    wyattt_ and gabrielw_unity like this.
  8. gabrielw_unity

    gabrielw_unity

    Unity Technologies

    Joined:
    Feb 19, 2018
    Posts:
    882
    @Catsoft-Studios - sure, a separate thread would be great, thanks :) I'm hearing from others here that other solutions might be better, maybe they/we can discuss in that thread :)
     
    mariandev and Catsoft-Studios like this.
  9. Catsoft-Studios

    Catsoft-Studios

    Joined:
    Jan 15, 2011
    Posts:
    672
  10. Foriero

    Foriero

    Joined:
    Jan 24, 2012
    Posts:
    569
    @gabrielw_unity Can we use the new tooling system in a production project? We are 2021.1 Thanks, Marek.
     
    Ruchir likes this.
  11. Catsoft-Studios

    Catsoft-Studios

    Joined:
    Jan 15, 2011
    Posts:
    672
    We've been playing witht he Overlay system and it's awesome. It takes some time to get used to it and the contrast between toggles when they are on/off it's a bit too subtle, but overall it's great and a big step forward. More so knowing that can (or will? haven't tried this yet) be used in other EditorWindows.

    One thing we haven't been able to do is to use the BuiltinToolsStrip visual element, along with the ToolButton, to create a chain of buttons that mimics the ones made by Unity.

    @gabrielw_unity - I guess we can create our own stylizing, but I'm wondering whether these will be exposed soon, so we can leverage them in our own tools?
     
    Ziflin likes this.
  12. gabrielw_unity

    gabrielw_unity

    Unity Technologies

    Joined:
    Feb 19, 2018
    Posts:
    882
    2021.2 is where the visuals landed, but the foundation is all there for 2021.1. I'd say upgrade if you can! :D
     
  13. gabrielw_unity

    gabrielw_unity

    Unity Technologies

    Joined:
    Feb 19, 2018
    Posts:
    882
    Hey! :) I would assume that's either on the way, or something else to solve that problem. I'll check!
     
    Catsoft-Studios likes this.
  14. BennyKokMusic

    BennyKokMusic

    Joined:
    Dec 22, 2016
    Posts:
    30
    Quick question, is that the Icon attribute only looks for the path in the "Editor Default Resources" folder?

    How should we go about in a situation that the icon is located in the Packages folder instead of the Assets folder?

    Code (CSharp):
    1. [Overlay(typeof(SceneView), k_OverlayID, k_DisplayName)]
    2. [Icon("proarray-icon.png")]
    3. internal class ProArrayOverlay : IMGUIOverlay
    4. {
    5. ...
    6. }
    upload_2021-5-20_11-46-31.png
     
  15. Catsoft-Studios

    Catsoft-Studios

    Joined:
    Jan 15, 2011
    Posts:
    672
    It looks from the project root. So if you have a texture inside the Assets folder called "MyTexture.png" you use

    Code (CSharp):
    1. [Icon("Assets/MyTexture.png")]
    If your texture is inside the Packages folder, use the "Packages/..." prefix
     
    kaarrrllll and BennyKokMusic like this.
  16. BennyKokMusic

    BennyKokMusic

    Joined:
    Dec 22, 2016
    Posts:
    30
    Thanks! Oh, no wonder, just also figured out the path for individual packages is with the package name instead of the actual folder name. It is working!

    Code (CSharp):
    1. [Overlay(typeof(SceneView), k_OverlayID, k_DisplayName)]
    2. [Icon("Packages/com.bennykok.proarray/Editor/Icons/proarray-icon.png")]
    3. internal class ProArrayOverlay : IMGUIOverlay
    4. {
    5.     ...
    6. }
    Now I'm wondering how to properly handle the icon if the package can be installed both in the Assets folder and Packages folder, after looking at @Catsoft-Studios 's previous reply about extending the IconAttribute, I end up doing something like this, and seems like its working.

    Code (CSharp):
    1. [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
    2. [Conditional("UNITY_EDITOR")]
    3. public class ProArrayIconAttribute : IconAttribute
    4. {
    5.     public ProArrayIconAttribute() : base(GetRelativeIconPath()) {}
    6.     public static string GetRelativeIconPath(string asmdef = "BennyKok.ProArray.Editor")
    7.     {
    8.         var p = Path.GetDirectoryName(CompilationPipeline.GetAssemblyDefinitionFilePathFromAssemblyName(asmdef));
    9.         return p + "/Icons/proarray-icon.png";
    10.     }
    11. }
    12.  
    13. [Overlay(typeof(SceneView), k_OverlayID, k_DisplayName)]
    14. [ProArrayIcon]
    15. internal class ProArrayOverlay : IMGUIOverlay
    16. {
    17. ...
    18. }
     
    Catsoft-Studios likes this.
  17. owen_proto

    owen_proto

    Joined:
    Mar 18, 2018
    Posts:
    109
    there doesn't seem to be a userControlledVilsibility property as mentioned in the original post. if that property won't be available, another potential solution to my issue would be only allowing my overlay in my custom PreviewSceneStage somehow (implementing ISupportsOverlays and using it as the editor window type in the overlay attribute does not work).

    having purpose-built overlays for custom PreviewSceneStage classes seems like a perfect match. it would be a powerful feature to add if it isn't already possible somehow.
     
    Last edited: Aug 25, 2021
  18. SoxwareInteractive

    SoxwareInteractive

    Joined:
    Jan 31, 2015
    Posts:
    505
    @gabrielw_unity the new overlays seem to break an "old" way of drawing windows in the scene view (using GUILayout.Window) that I've been using in my tools. Is this on purpose?

    To reproduce this, put the following script in an editor folder in Unity 2021.2(b13). Then click on "Window --> Start Test". Notice how no window is shown in the scene view. In the console, "GUILayout.Window() called" is shown and "Callback called" is not shown. Then execute the same script in Unity 2021.1 and notice how the window is drawn correctly.
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using UnityEditor;
    4.  
    5. public class Test
    6. {
    7.     [MenuItem("Window/Start Test")]
    8.     public static void MenuItemClicked()
    9.     {
    10.         SceneView.duringSceneGui += OnSceneGUI;
    11.     }
    12.  
    13.     private static void OnSceneGUI(SceneView sceneView)
    14.     {
    15.         Handles.BeginGUI();
    16.  
    17.         Debug.Log("GUILayout.Window() called");
    18.         GUILayout.Window(2, new Rect(sceneView.position.width - 110, sceneView.position.height - 130, 100, 100), WindowCallback, "Title");
    19.  
    20.         Handles.EndGUI();
    21.     }
    22.  
    23.     private static void WindowCallback(int id)
    24.     {
    25.         Debug.Log("Callback called"); // <-- not called in Unity 2021.2b13
    26.         GUILayout.Button("A Button");
    27.     }
    28. }
    29.  
    Is this behavior on purpose or is this a bug?

    Thanks.
     
  19. kaarrrllll

    kaarrrllll

    Unity Technologies

    Joined:
    Aug 24, 2017
    Posts:
    477
    This was a regression, there is a PR in flight to correct it.
     
    SoxwareInteractive likes this.
  20. SoxwareInteractive

    SoxwareInteractive

    Joined:
    Jan 31, 2015
    Posts:
    505
    Ok great, thanks for looking into this. Btw. this is the related bug report ticket (guess you can eventually close this):1369148
     
    kaarrrllll likes this.
  21. Ricardo42

    Ricardo42

    Joined:
    Jun 12, 2018
    Posts:
    3
    Hey I've just started trying out Overlays in Unity 2021.2, but I'm having a hard time figuring out how to recreate the default toolbar behavior, where multiple toggles seem to be related, and only one can be pressed at one time. How would I go about recreating that?
     
  22. samanabo

    samanabo

    Joined:
    Mar 10, 2015
    Posts:
    11
  23. SoxwareInteractive

    SoxwareInteractive

    Joined:
    Jan 31, 2015
    Posts:
    505
    Hey @kaarrrllll,
    just wanted to let you know that even though the issue tracker (https://issuetracker.unity3d.com/product/unity/issues/guid/1358677) says that the bug I mentioned earlier is already fixed in Unity 2021.2.x, I can still reproduce it in Unity 2021.2.1f1 with the code example I've provided in my bug report (1369148).

    Another Unity user is also seeing the same behavior (see comment in the issue tracker). May I ask you to please re-visit this bug and check what went wrong? Thank you very much.
     
  24. kaarrrllll

    kaarrrllll

    Unity Technologies

    Joined:
    Aug 24, 2017
    Posts:
    477
    I see that this issue was closed as fixed in 2021.2.1f1, but also that the fix introduced a regression that was subsequently fixed in 2021.2.3f1. Testing on 2021.2.1f1, I confirm that GUI.Window is not working when called from the static SceneView delegate. I tested again on 2021.2.3f1, and verified that it is working as expected.
     
  25. SoxwareInteractive

    SoxwareInteractive

    Joined:
    Jan 31, 2015
    Posts:
    505
    Thank you so much for looking into this and for the explanation what happened behind the scenes. I can confirm that in Unity 2021.2.3f1 everything is functioning as expected again :)
     
    kaarrrllll likes this.
unityunity