Search Unity

Showcase Tip: Custom EditorTool with Window

Discussion in 'Scripting' started by Qriva, Jun 11, 2021.

  1. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,314
    I guess many of you already discovered EditorTool API, but in case you didn't, I wanted to share some bits of code - it's not game changer, but maybe it will be useful for some of you.

    If this is first time you hear about EditorTools, here is very short explanation:
    EditorTool class lets you register new specialized (contextual) or global scene tool (buttons on the left top).
    For example move or rotate is global tool while editing box collider size is contextual tool.
    The great thing about tools is they work like modes and there can be only one of them active at the same time, so when you edit collider bounds there will be no move handles. You can read more here.


    You can probably see already where it can be useful - spline editing tool or any painting or placing tools are great examples, but what if you need additional settings?
    Recently I needed to make 3D tile editor. It needed window with settings and tile palette, but brush had to behave like scene tool. I would end up with problematic custom inspector or window with SceneView.duringSceneGui event, but instead I combined EditorTool and EditorWindow and it worked very well.

    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEditor.EditorTools;
    3.  
    4. // Custom Editor Tool for MagicComponent component
    5. [EditorTool("Custom Tool", typeof(MagicComponent))]
    6. public class MagicTool : EditorTool
    7. {
    8.     // Reference to settings window
    9.     private MagicToolSettingsWindow window;
    10.    
    11.     public override void OnActivated()
    12.     {
    13.         // MagicToolSettingsWindow is required to work, so open it when tool gets activated
    14.         // When window is docked as tab, then it will be focused instead
    15.         window = EditorWindow.GetWindow<MagicToolSettingsWindow>();
    16.     }
    17.  
    18.     public override void OnToolGUI(EditorWindow sceneWindow)
    19.     {
    20.         // Window must be SceneView
    21.         if (!(sceneWindow is SceneView sceneView))
    22.         {
    23.             return;
    24.         }
    25.  
    26.         // You can access settings now
    27.         int foo = window.someSetting;
    28.  
    29.         // Do your stuff [...]
    30.     }
    31.  
    32.     // [...]
    33.  
    34.     // Bonus shortcut to activate tool when scene is focused and 'M' pressed
    35.     [ShortcutManagement.Shortcut("Magic Tool", null, KeyCode.M)]
    36.     static void ToolShortcut()
    37.     {
    38.         if (Selection.GetFiltered<MagicComponent>(SelectionMode.TopLevel).Length > 0)
    39.         {
    40.             ToolManager.SetActiveTool<MagicTool>();
    41.         }
    42.     }
    43. }
    44.  
    45. // Tool Settings Window
    46. public class MagicToolSettingsWindow : EditorWindow
    47. {
    48.     public int someSetting = 3;
    49.  
    50.     [MenuItem("Tools/Magic Tool Window")]
    51.     public static void OpenWindow()
    52.     {
    53.         MagicToolSettingsWindow window = GetWindow<MagicToolSettingsWindow>();
    54.     }
    55.  
    56.     void OnDisable()
    57.     {
    58.         // Check if EditorTool is currently active and disable it when window is closed
    59.         // MagicTool requires this window to be open as long as it's active
    60.         if (ToolManager.activeToolType == typeof(MagicTool))
    61.         {
    62.             // Try to activate previously used tool
    63.             ToolManager.RestorePreviousPersistentTool();
    64.         }
    65.     }
    66.  
    67.     // [...]
    68. }
    69.  

    Advantages of this method:
    • Tool settings and logic are decoupled from components and their inspectors
    • It works properly even when editor window is closed or not focused or tab not active
    • No need to bother with default handles or any other active tools as only yours is active
    • Implementing IDrawSelectedHandles lets you draw gizmos, even when tool is not active
    • When you activate tool, the settings window will show up or focus automatically
    • Tool can be activated only when selection is compatibile
    Probably it can be done even better, I would be happy to see some improvements and other similar "tricks".
    Also I heared there is new scene api in 2021 and this and similar ideas can be pushed even furter, but I didn't try it yet. Example above works in 2020.3 and I think it should work in 2019.4 too.
     
  2. mustmiekieke

    mustmiekieke

    Joined:
    Mar 2, 2015
    Posts:
    1
    Amazing! Exactly what I was looking for :)