All UI under one gameobject or several?

Hi,

First, I must say that UI Elements is really starting to work for me. I’m long past the teething pains of earlier versions. It’s a pretty superior workflow.

Question: Is it best to pool all UI under one gameobject, or several? I currently mount all UI panels under one root, and then attach stylesheet unique for each document. So e.g. two info panels are two top-level children under this single root, and each has a unique stylesheet attached. The root also has a universal stylesheet attached.

I’m not having any problems, I just want to understand pros and cons with actually instantiating a unique gameobject for each major UI item, e.g. a separate gameobject for modal dialogs.

My guess is that it’s best to keep pooling, because UI Elements gets to reason about the UI from the perspective of a single root. I’d also assume that you can’t stop propagation of events in a multi-root world, so one panel would be unable block clicks from another. I’m also not sure how the render order is controlled.

Maybe this is an idiot question because I’ve never actually done multiple roots. I just wanted to affirm that single root is correct.

1 Like

These are good questions, and we would like to be able to formulate good recommendations for that.

One clarification that I would like to make:

  • Multiple Panel Settings really mean multiple top level roots with no relation between them, hence:
    – each panel has its own VisualElement root (with parent == null)
    – events are dispatched once per panel (sorted with PanelSettings’s Sort order and until it’s handled)
    – one panel is a least its own draw call
  • Multiple UIDocument components with the same PanelSettings are like VisualElements parented into the root element of the Panels, hence:
    – multiple UIDocument at the same level end up producing sibling VisualElement
    – multiple UIDocument are batched (when possible)

So there are 3 “levels” to think about:

  • Panel (via PanelSettings)
  • UIDocument
  • raw Visual Elements

While Panel vs. UIDocument has a lot of performance implications, UIDocument vs. raw Visual Elements will not make a huge difference and I would encourage using what makes the most sense for you in terms of workflow.

The extra cost to be aware of when using UIDocument is:

  • Disabling/enabling the GameObject owning the UIDocument releases/remove and recreates/re-adds the hierarchy
  • UIDocuments need to be sorted based on their sortOrder

But other than that, it’s mostly a question of preference and desired workflow, also taking into consideration how complex your game or app is and how you want things organized.

I could expand on project organization but I am under the impression that your question is mostly about performance, right?

2 Likes

Hi,

It’s more about just developing an intuition of right and wrong. Since I have 3 completely separate panels, the question came up.

It seems to me that it’s mostly downside when having multiple gameobjects, but that it’s partly mitigated by using the same PanelSettings?

But otherwise, there’s nothing you can do with multi-root that you can’t do with single root, and you get absolute control over the stacking order, event propagation, and the draw calls and raycasting are one-stop-shop. It kind of doesn’t make sense to have a unified, hierarchical event system, and then create multiple roots that shift the whole burden back on you to make one aware of the other.

So I feel like I have my answer, one root is better.

On the issue of draw calls, is there any penalty to having large VisualElements to be used as DIVs? I find that most UI layout for me starts with my top-level nodes right under root have a VisualElement that fills the screen as the first order of business, and then items are laid out using flex inside of that.

So even if I have a button in the center of the screen, it would be wrapped in a full screen empty VisualElement with flex: column and the right alignment options to center it horizontally and vertically.

Is there a rendering penalty for very large but empty VisualElements?

Just so I understand this correctly, these two methods make no difference in performance?

Method 1:

7423532--908555--Screen Shot 2021-08-16 at 5.48.58 PM.png

The UIDocument has 3 menus in one as a template. This way they share a single UIDocument and Panel Settings.

Method 2:

Those 3 menus from method one would be split into their own UIDocument BUT still share the same Panel Settings.

Is this literally the same thing in terms of performance?

No, if they are empty, they do not contribute anything to rendering. Having many empty elements can affect other systems, but once the UI is “stable” it won’t change anything to draw calls.

Like I said the UIDocument has an extra cost, but once the UI has been created, there is no distinction for UI Toolkit if the Visual Elements came from separate UIDocuments or one.

You could compare the hierarchies with the UI Debugger to get a feeling for it.

2 Likes

Hello Antoine. I’m just winging it right now. When certain buildings are clicked in game, relevant UI document game objects are enabled/disabled. Some of the UI take up the same location so they are divided into different documents.
7426319--909122--ui layout.png

It works but is there a recommended way? Any specific or general organization tips would help.

I’ve gone 100% in the opposite direction, and I’ve built a framework for having everything under one game object. My reasoning is:

  • Fewer draw calls.
  • Predictable and controllable visual layering.
  • Predictable and controllable event layering.
  • The ability to mask events by having e.g. a modal dialog inside a plane that fills the screen, preventing all other clicks.

If anyone is interested, I set up the root UI object like this:

   private static GameObject uiGameObj;
    private static UIDocument rootDoc;
    private static PanelSettings panelSettings;

    public static void Init()
    {
        if (uiGameObj != null) {
            return;
        }

        uiGameObj = new GameObject();
        uiGameObj.name = "UI Container";

        rootDoc = uiGameObj.AddComponent<UIDocument>();
        root = rootDoc.rootVisualElement;
        root.StretchToParentSize();
       
        panelSettings = Resources.Load<PanelSettings>("PanelSettings");

       if (panelSettings == null) {
            Debug.Log("No panel settings found");
        }

        rootDoc.panelSettings = panelSettings;
    }

And then I instantiate panels as needed, some permanently, and some added when they’re needed, all added as immediate children to the same root.

    private void AddBottomBar()
    {
        var visualTree = Resources.Load<VisualTreeAsset>("Panels/BottomBar Panel");
        bottomBar = visualTree.Instantiate();
        bottomBar.styleSheets.Add(Resources.Load<StyleSheet>("Panels/BottomBar Style"));
        UI.root.Add(bottomBar);
        bottomBar.StretchToParentSize();
        bottomBar.pickingMode = PickingMode.Ignore;
1 Like