Search Unity

Is UIElements much more work for simple editors or am I not using the API efficiently?

Discussion in 'UIElements' started by Xarbrough, Jul 3, 2019.

  1. Xarbrough

    Xarbrough

    Joined:
    Dec 11, 2014
    Posts:
    504
    I'm already convinced that UIElements will be great for complex editor windows and special custom editors, however, after trying to convert a few simple editor tools I kind of think that it will be much more code and honestly more work to use UIElements.

    Is this only my perception or would you agree that UIElements is better suited for more complex cases?

    As an example, here is my old IMGUI code:

    Code (CSharp):
    1. namespace MyNamespace
    2. {
    3.     using UnityEditor;
    4.     using UnityEngine;
    5.  
    6.     public class TimeScaleWindow : EditorWindow
    7.     {
    8.         private const string windowTitle = "Time Scale";
    9.  
    10.         [MenuItem("Puk/" + windowTitle)]
    11.         public static void ShowWindow()
    12.         {
    13.             var window = GetWindow<TimeScaleWindow>(windowTitle);
    14.             window.minSize = new Vector2(30f, EditorGUIUtility.singleLineHeight + 2f);
    15.         }
    16.  
    17.         private float maxTime = 10f;
    18.         private float timeScale;
    19.         private GUIContent resetIcon;
    20.         private GUIStyle resetButtonStyle;
    21.  
    22.         private void OnEnable()
    23.         {
    24.             timeScale = Time.timeScale;
    25.             var icon = (Texture2D)(EditorGUIUtility.isProSkin ?
    26.                 EditorGUIUtility.Load("d_LookDevResetEnv") : EditorGUIUtility.Load("LookDevResetEnv"));
    27.             resetIcon = new GUIContent(icon);
    28.         }
    29.  
    30.         private void OnDisable()
    31.         {
    32.             timeScale = 1f;
    33.         }
    34.  
    35.         private void OnGUI()
    36.         {
    37.             if (resetButtonStyle == null)
    38.             {
    39.                 resetButtonStyle = new GUIStyle(EditorStyles.miniButton)
    40.                 {
    41.                     padding = new RectOffset(),
    42.                     margin = new RectOffset()
    43.                 };
    44.             }
    45.  
    46.             GUI.enabled = Application.isPlaying;
    47.  
    48.             Rect rect = EditorGUILayout.GetControlRect();
    49.             rect.width -= 59;
    50.  
    51.             EditorGUI.BeginChangeCheck();
    52.             timeScale = EditorGUI.Slider(rect, GUIContent.none, timeScale, 0f, maxTime);
    53.             if (EditorGUI.EndChangeCheck())
    54.                 Time.timeScale = timeScale;
    55.  
    56.             rect.x = rect.xMax + 4;
    57.             rect.width = 30;
    58.             maxTime = EditorGUI.FloatField(rect, GUIContent.none, maxTime);
    59.             if (maxTime < 0)
    60.                 maxTime = 0f;
    61.  
    62.             rect.x = rect.xMax + 3;
    63.             rect.width = 21;
    64.  
    65.             if (GUI.Button(rect, resetIcon, resetButtonStyle))
    66.             {
    67.                 Time.timeScale = 1f;
    68.                 timeScale = 1f;
    69.             }
    70.  
    71.             GUI.enabled = true;
    72.         }
    73.     }
    74. }
    It's really no masterpiece, but it only took 5-10 minutes to write and does what it needs to do, looks fine and all.

    Here is the same window in UIElements:

    Code (CSharp):
    1. namespace MyNamespace
    2. {
    3.     using System;
    4.     using UnityEditor;
    5.     using UnityEditor.UIElements;
    6.     using UnityEngine;
    7.     using UnityEngine.UIElements;
    8.  
    9.     public class TimeScaleWindow_Elements : EditorWindow
    10.     {
    11.         private const string windowTitle = "Time Scale";
    12.  
    13.         [MenuItem("Puk/" + windowTitle + " (Elements)")]
    14.         public static void ShowWindow()
    15.         {
    16.             var window = GetWindow<TimeScaleWindow_Elements>();
    17.             window.minSize = new Vector2(30f, EditorGUIUtility.singleLineHeight + 6f);
    18.             window.titleContent = new GUIContent(windowTitle);
    19.         }
    20.  
    21.         [SerializeField]
    22.         private StyleSheet styleSheet;
    23.  
    24.         private void OnEnable()
    25.         {
    26.             EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
    27.  
    28.             var root = base.rootVisualElement;
    29.             root.styleSheets.Add(this.styleSheet);
    30.  
    31.             var container = new VisualElement();
    32.             container.name = "Container";
    33.             container.SetEnabled(Application.isPlaying);
    34.  
    35.             var slider = new Slider(0f, 10f);
    36.             slider.value = (float)Math.Round(Time.timeScale, 2);
    37.             container.Add(slider);
    38.  
    39.             var timeField = new FloatField(3);
    40.             timeField.value = slider.value;
    41.             timeField.RegisterValueChangedCallback(evt =>
    42.             {
    43.                 float value = (float)Math.Round(Mathf.Max(0, evt.newValue), 2);
    44.                 Time.timeScale = value;
    45.                 slider.value = value;
    46.             });
    47.             container.Add(timeField);
    48.  
    49.             slider.RegisterValueChangedCallback(evt =>
    50.             {
    51.                 float value = (float)Math.Round(Mathf.Max(0, evt.newValue), 2);
    52.                 Time.timeScale = value;
    53.                 timeField.SetValueWithoutNotify(value);
    54.             });
    55.  
    56.             var maxTimeField = new FloatField(3);
    57.             maxTimeField.value = slider.highValue;
    58.             maxTimeField.RegisterValueChangedCallback(evt =>
    59.             {
    60.                 slider.highValue = evt.newValue;
    61.             });
    62.             container.Add(maxTimeField);
    63.  
    64.             var button = new Button();
    65.             var icon = new VisualElement();
    66.             button.clickable.clicked += () =>
    67.             {
    68.                 Time.timeScale = 1f;
    69.                 slider.value = 1f;
    70.                 timeField.SetValueWithoutNotify(1f);
    71.             };
    72.             button.Add(icon);
    73.             container.Add(button);
    74.  
    75.             root.Add(container);
    76.         }
    77.  
    78.         private void OnPlayModeStateChanged(PlayModeStateChange mode)
    79.         {
    80.             if (mode == PlayModeStateChange.EnteredPlayMode)
    81.                 rootVisualElement.Q("Container").SetEnabled(true);
    82.             else if (mode == PlayModeStateChange.ExitingPlayMode)
    83.                 rootVisualElement.Q("Container").SetEnabled(false);
    84.         }
    85.  
    86.         private void OnDisable()
    87.         {
    88.             Time.timeScale = 1f;
    89.             EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
    90.         }
    91.     }
    92. }
    And the required stylesheet:

    Code (CSharp):
    1. #Container > * {
    2.     margin-left: 2;
    3.     margin-right: 2;
    4. }
    5.  
    6. #Container {
    7.     flex-direction: row;
    8.     margin-top: 2;
    9.     margin-bottom: 2;
    10. }
    11.  
    12. FloatField {
    13.     width: 40;
    14.     margin-left: 20;
    15.     align-self: center;
    16. }
    17.  
    18. Slider {
    19.     flex-grow: 1;
    20.     padding-left: 2;
    21.     padding-right: 2;
    22. }
    23.  
    24. Button {
    25.     width: 21;
    26.     height: 19;
    27.     padding-top: 2;
    28.     margin-top: 0;
    29. }
    30.  
    31. Button > VisualElement {
    32.     width: 12;
    33.     height: 12;
    34.     background-image: resource("d_LookDevResetEnv");
    35. }
    This took much longer to write and still isn't perfect (something about the margins and padding is still off when scaling, etc.).

    Please let me know if I simply haven't grasped the full power of the new API or see this as my feedback that we need more high-level functionality.

    I also am aware of the fact that I can call IMGUI code from within UIElements, but as far as I understand it, this feature is only for transitioning and the end goal will be to convert everything to the new system, so I'm not comparing shortcuts like this.

    Here are a few points I think are lacking:
    • Enabling/Disabling elements depending on the playmode state is much easier in IMGUI, because I simply update a single value GUI.enabled each frame
    • The same goes for hiding and unhiding elements depending on some state (e.g. only show a portion of the UI if a bool is checked)
    • The slider does not include a float field, but the IMGUI version does. I need to manually create my own composite control for this if I want to reuse it.
    • There should be a default button class that allows an icon to be set
    • All of the high-level controls should come with styling that matches the IMGUI controls from the legacy editor code
    This of course is only my humble opinion just having started out with the system, but I still want to share my experience in case the workflow can be improved or in case I can learn a few tricks from more experiences forum members. :)
     
    awesomedata likes this.
  2. rastlin

    rastlin

    Joined:
    Jun 5, 2017
    Posts:
    100
    I would wager, that such observations are always present when transitioning to a framework with more robust foundations. You could have similar observations when transitioning to MVC from "legacy" approaches to web development, similarly moving from WinForms to WPF.

    The key think to have in mind is that ultimately UIElements is NOT supposed to speed up your simple workflows. You would always have an easier time just mashing things together with IMGUI for your simple use cases.

    The UIElements improves manageability and extensibility of UI controls and tremendously improves ability to follow the implementation flow/logic. That's the main benefit of such system. Dooring things "the right" way always takes more time then doing them fast.

    In time, controls will be improved, and users can write their own controls and share them simply with others through style files.
     
    Xarbrough likes this.
  3. Xarbrough

    Xarbrough

    Joined:
    Dec 11, 2014
    Posts:
    504
    I think this is the main thing I'm hoping and waiting for, that we will be able to re-use prebuilt controls such as a slider and float field or a button with a simple icon etc.
     
  4. etienne_phil_unity

    etienne_phil_unity

    Unity Technologies

    Joined:
    Jan 15, 2019
    Posts:
    15
    It is true that for a reasonably simple editor, using UIElements is not necessarily faster than IMGUI (in terms of development time, because in terms of performance, UI Elements is faster than IMGUI) Actually putting together simple UI quickly is the forte of IMGUI. That being said, UIElements has a couple of advantages over IMGUI, it clearly separates styling, logic, etc.... This decoupling makes maintenance and code reuse simpler (as @rastlin mentioned). With IMGUI, state management quickly becomes tedious and error prone, as all UI code ends up altering a global state. When said state is not what you expect, tracking down the culprit can get hard fast. To simplify the use of UIElements, we're currently working on a visual editor. You can also use the UI Elements debugger. Thanks for your feedback :)
     
    Sylmerria and Xarbrough like this.
  5. DGordon

    DGordon

    Joined:
    Dec 8, 2013
    Posts:
    411
    What you're saying makes sense ... UT just needs to make sure simple things don't become more and more complicated as Unity is made "better".

    I'm happy to learn the new system ... and the new SRP ... and so on. However, as one of our artists was mentioning to me, he really hopes Unity isn't going to become overly complicated (in reference to HDRP vs vanilla), which, when dealing with small companies that do well when people wear multiple hats ... becomes a barrier to having artists learn how to do minor tasks in non artist things, and so on.

    I guess my point is, improving Unity to scale upwards is great! However, if you can find a way to do that without raising the entry point for people who don't need that ... please put in the effort to do so. Especially when just about _everything_ in Unity is getting improved to scale upwards.

    I'd love to teach several of my co-workers how to write a custom editor to improve their workflows in simple ways without me ... but the more hoops there are to jump through, the less likely that becomes. I'm assuming we'll end up with UI Elements having simple default ways of creating what we already can create just as easily, while giving us the flexibility of doing more stuff, and in better ways.

    [Edit -- I'll mention that we tend to create 2d games that run on school computers ... so scaling upwards isn't really our biggest issue. Creating things easily and quickly is much more important, since Unity already handles whatever we're building just fine.]
     
    Last edited: Jul 15, 2019
  6. MCrafterzz

    MCrafterzz

    Joined:
    Jun 3, 2017
    Posts:
    287
    If the old system works for you (it does for me) then I don't see a reason to change as the new system is more complicated. It's defently better for super advanced stuff but I haven't needed that yet. Also the old system probably will be left for years so I don't believe that we'll have to convert our stuff to the new system anytime soon. Just look at some of the deprecated methods that have existed for MANY years....
     
    tcmeric likes this.
  7. DGordon

    DGordon

    Joined:
    Dec 8, 2013
    Posts:
    411
    Sure. I agree ... but Im still concerned that the overall direction of Unity doesnt grow more and more complicated. Im hoping we can still use this easily in 10 years for educational (not aaa 3d) games and not have to do things in more complicated ways than our use cases require, while looking back wistfully at the good old days ;).
     
    MCrafterzz likes this.
  8. DGordon

    DGordon

    Joined:
    Dec 8, 2013
    Posts:
    411
    To be clear, all Im pointing out is we should hopefully wind up with an API that allows us to use uielements to build simple editors as quickly and easily as we currently can. Im all for having a more robust and faster system.
     
    MCrafterzz likes this.
  9. MCrafterzz

    MCrafterzz

    Joined:
    Jun 3, 2017
    Posts:
    287
    Noone is against a faster system :p
     
  10. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    814
    Is this even being considered right now for UIElements though?


    Future workflows should be being considered right now because too many design decisions not considered early enough requires e.g. an entire rewrite of a whole interdependent system (like UI and shortcuts and visual authoring tools!) You guys are notorious for not knowing how far to reach with your systems, excluding this or that possibility.

    Personally, I'd love to abandon IMGUI for UIElements entirely. A visual authoring and layout solution would take a load of tedium off my plate, assuming serialization between Editor and runtime is being handled too.

    The recently posted video showing UIElements off was a bit of a sleight of hand.
    It was very quick, skipped over very important bits of code to make even that simple (but complicated) example function that even novice programmers would have a very hard time understanding without a very clear explanation. What about artists and designers??


    Without a fast easy-to-understand approach to just that much tool building, you also run into common tool design cases like:

    1. Tabbed views
    2. UI shortcuts to particular controls (like to buttons, menus, etc.)
    3. Multi-tabbed, multi-screen, multi-window setups (see Snapcam's dependent windows)
    4. Floating dockable changing window content and controls
    5. Scene-based shortcuts and tools that interact with the window contents and controls
    6. Interactive graphics in the window like the humanoid setup diagram or the spriteshape angle circle.
    7. Finally, getting all of that into a node on a zoomable graph, with functional zooming (potentially zooming inside the node itself), with special controls like reorderable lists, listboxes, etc. all functioning properly, and placing a nice image behind all of that (that also handles zooming well).

    You guys have your work cut out for you.

    Without showing examples of the process involved in achieving these kinds of things, people will still be very hesitant to fully embrace the new system. Please show us an easy workflow to accomplish all these things.
     
    Last edited: Jul 29, 2019
    dadude123, MCrafterzz and Sylmerria like this.
  11. Stardog

    Stardog

    Joined:
    Jun 28, 2010
    Posts:
    1,322
    It uses two languages that all web designers use, so I think designers will be fine.

    As far as an editor is concerned, I hope the Unity dev's have used Webflow and take inspiration from it so we don't end up with some unsemantic trash drawing program.
     
    Last edited: Aug 12, 2019
    awesomedata likes this.
  12. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    814
    Web designers != All designers


    Just saying.


    There are pros and cons to using web development as a basis for interface design, but intuitiveness is definitely not a selling point of xml or css.

    I know plenty of really great designers who don't even know basic HTML, yet can envision sleek animated interfaces with their eyes closed. Hand them notepad++ and tell them they must code in an easy to understand way, and they will tell you to get lost because they don't care how efficient it is in the long-run -- they didn't sign up to be programmers. :/

    Just food for thought.
     
  13. DGordon

    DGordon

    Joined:
    Dec 8, 2013
    Posts:
    411
    Also throwing this out there ... not everyone who _needs_ to use this is a really great designer. I'm a programmer ... but because we're a small company, I wear many hats. A _major_ part of what I do is making things easy for everyone else to do their jobs ... which in Unity, means lots and lots of editor tooling. My job isnt about making things look sleek and animated ... its about getting the functionality that everyone needs into the places they need it to make doing their jobs as easy as possible.

    So ... making it harder to do that, while making it easier to make large scale stuff look nice or have less bugs, is actually a losing situation for me. I have an entire suite of editors, from Dialogs, Quests, Scenes, Audio, Streaming Images, and a whole slew of other data editors, which handles everything from generating placeholder VO for all necessary text, to generating C# files so scripting snippets can be done easily in an editor. Its a lot of work for one programmer, and on the one hand, making that _easier_ to manage is good ... on the other hand, making it more verbose to write is bad. We depend on the editor tooling, so making it better is great ... but making it better for teams >= X, but worse for smaller teams, is bad.

    So yeah ... I don't care about what web designers use. I'm not a web designer. I have no desire to become one. If I have to learn new stuff to use this ... no problem. However, I'm hoping they aren't going to make this harder for everyone who isn't a web designer, and has no need for all the nifty new features. I just need this to (a) work, and (b) not require a ton of setup for stuff that should be really simple. I definitely see a lot of issues with the current way of building editors ... but for all that, I can very quickly get something functional up and running that does what it needs to do.

    Just drawing attention to the fact that I am in no way a designer or artist, nor a web designer, yet this falls on me. I'm sure I'm not the only programmer who has to be the one creating the editors. I really don't believe I'm the minority here, and that the vast majority of users programming editors are either web designers or part of a large enough team to warrant delegating this stuff to the people who really know how to leverage the power that I'm sure its going to offer. So ... hoping we don't fall through the cracks as they make stuff better. There's a very obvious push to making Unity a more viable solution to larger teams ... I just don't want to see it become a less viable solution for smaller teams because of that.

    TLDR: just don't lose how simple/quick it is to build small editors as you make building more complex editors easier. (Yes, I can learn whatever they make me learn ... but it doesn't mean I want to see it go down that route).
     
    Last edited: Aug 4, 2019
    awesomedata and MCrafterzz like this.
  14. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    814
    I like the way C# in VisualStudio can bind data fields using particular classes to structure them. Nested object instances of these classes generally do much better than HTML / XML nesting imo.

    It would be cool if these classes could then be visualized by inheriting from different styles of windows, elements, and other functional things inline, either by overriding certain functions or behaviors or by applying attributes to a certain thing.


    Is there any reason this path wasn't taken? -- Is this perhaps because of this being setup to also work for runtime UI?