Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Class architecture for tools which should be used as editor window or called from other code

Discussion in 'Immediate Mode GUI (IMGUI)' started by Xarbrough, Jan 15, 2017.

  1. Xarbrough

    Xarbrough

    Joined:
    Dec 11, 2014
    Posts:
    1,188
    I've been working on editor tools, which started out as simple editor windows, but now we also want to use them during automated build. This means that the actual tool should be functional without any GUI, but we also want the window when working in a specific scene and tweaking settings from a config file.

    For now I am calling the static GetWindow function to get an instance of my tool class, even when the window is not required. This doesn't feel right and also becomes an annoyance when several tools are run automatically and all windows pop up in the editor.

    Code (CSharp):
    1. public class MeshProcessor : EditorWindow
    2. {
    3.     [MenuItem("Window/Mesh Processor")]
    4.     public static MeshProcessor GetInstance()
    5.     {
    6.         return GetWindow<MeshProcessor>();
    7.     }
    8.  
    9.     public void DoWork()
    10.     {
    11.         // ...
    12.     }
    13. }
    14.  
    15. public static class AutomaticBuildPipeline
    16. {
    17.     // This is run in batch mode or as part of some other automated tool in the editor.
    18.     public static void Build()
    19.     {
    20.         // Works fine, but also shows the editor window, although we don't need user interaction.
    21.         MeshProcessor meshProcessor = MeshProcessor.GetInstance();
    22.         meshProcessor.DoWork();
    23.     }
    24. }
    What would be a better general architecture for this scenario? Is there a way to get an editor window instance without showing it? Or should I separate the tool functionality from the window?

    Code (CSharp):
    1. public class MeshProcessorWindow : EditorWindow
    2. {
    3.     [MenuItem("Window/Mesh Processor")]
    4.     static void ShowWindow()
    5.     {
    6.         GetWindow<MeshProcessorWindow>();
    7.     }
    8.  
    9.     MeshProcessor processor;
    10.  
    11.     void OnEnable()
    12.     {
    13.         processor = new MeshProcessor();
    14.         processor.DoWork();
    15.     }
    16. }
    17.  
    18. public class MeshProcessor
    19. {
    20.     public void DoWork()
    21.     {
    22.         // ...
    23.     }
    24. }
    25.  
    26. public static class AutomaticBuildPipeline
    27. {
    28.     public static void Build()
    29.     {
    30.         MeshProcessor meshProcessor = new MeshProcessor();
    31.         meshProcessor.DoWork();
    32.     }
    33. }
    The second approach feels better, but it needs a lot of boilerplate code in practice. There are quite a few user settings, which would need to be brought over from the user window to the MeshProcessor instance, meaning that they would most likely have duplicated properties. Maybe I could pass a common settings object, but then I'm unsure about where to put the GUI code to draw those properties.

    Thanks for your advice!
     
  2. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    This is the approach to go with. The window should rely on the logic defined elsewhere. That's true not only in Unity but in any other development environment.

    It is a wise move to separate logic from presentation. Presentation (the window) may change and could look like anything.

    The actual logic will remain the same.

    What i don't understand is what sort of boilerplate code will be needed in that case ?
     
    Xarbrough likes this.
  3. Xarbrough

    Xarbrough

    Joined:
    Dec 11, 2014
    Posts:
    1,188
    Thanks for you recommendation!

    Actually, I wasn't thinking too smartly about the issue. I thought I had to copy each property from the object to my window, but of course I could also just create my instance and then directly set those properties. Then I'd only have window-related fields other than the actual worker instance reference in my window class.