2
\$\begingroup\$

I'm using Unity UI Toolkit in Unity 2021.2.

Looking at the following example:

<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" xsi="http://www.w3.org/2001/XMLSchema-instance" engine="UnityEngine.UIElements" editor="UnityEditor.UIElements" noNamespaceSchemaLocation="../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="False">
  <!-- Creates our template and gives it a name for future reference. -->
  <ui:Template src="Assets/Editor/Resources/ButtonTemplate.uxml" name="button-template" />
  <!-- Creates a parent VisualElement inside which we will use our button template. -->
  <ui:VisualElement class="buttons-container">
    <!-- Instantiates the template multiple times. Each time, we give it a name for future reference. -->
    <ui:Instance template="button-template" name="Cube"/>
    <ui:Instance template="button-template" name="Sphere"/>
    <ui:Instance template="button-template" name="Capsule"/>
    <ui:Instance template="button-template" name="Cylinder"/>
    <ui:Instance template="button-template" name="Plane"/>
  </ui:VisualElement>
</ui:UXML>

The question is if/how one can create those instances of a template from code instead of having to add them manually. The final result I would like to use it on is not a button, but a different template. Another criterion is that it should be buildable while not in editor window, because that is somewhat explained here

\$\endgroup\$
3
  • \$\begingroup\$ Have you tried using a VisualTreeAsset? It's basically a serialized Visual Element Tree. Instantiate it and child the resulting root node to the UIdocument of your scene. Then you can query for elements and change values procedurally. \$\endgroup\$
    – Charly
    Commented Feb 27, 2022 at 22:48
  • \$\begingroup\$ I want to do that, but the code to load such a VisualTreeAsset that i found previously only work in the editor mode :( ``` AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/menu.uxml"); EditorGUIUtility.Load("Assets/menu.uxml") as VisualTreeAsset; ``` if you know another way that doesn't require the editor, I'm all ears! \$\endgroup\$
    – TheWolfNL
    Commented Feb 28, 2022 at 8:12
  • \$\begingroup\$ Added an answer with a code snippet which addresses your loading concerns. \$\endgroup\$
    – Charly
    Commented Feb 28, 2022 at 9:34

1 Answer 1

3
\$\begingroup\$
Solution

You can use a VisualTreeAsset. It's a serialized Visual Element Tree. Instantiate it and child the resulting root node to the UIdocument of your scene. Then you can query for elements and change values procedurally.

class TestMono : MonoBehaviour{
        //TODO: assign these members in the inspector
        [SerializeField] VisualTreeAsset myTreeAssetMember;
        [SerializeField] UIDocument UIDocument;

        private void Start()
        {
            TemplateContainer myUI = myTreeAssetMember.Instantiate();
            
            //an example of querying for the root element of a button template with the id "submit"
            Button button = myUI.Q<Button>("submit");
            if (button != null)
             button.clickable = new Clickable(() => Debug.Log("Stub submit button clicked."));
            else
                Debug.LogWarning($"Could not find \"submit\" button.");
            
            //child the template to the UIDocument so it will be rendered and updated
            UIDocument.rootVisualElement.Add(myUI);
        }
    }

enter image description here

On Asset Loading Concerns

I want to do that, but the code to load such a VisualTreeAsset that i found previously only work in the editor mode :( AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/menu.uxml"); EditorGUIUtility.Load("Assets/menu.uxml") as VisualTreeAsset; if you know another way that doesn't require the editor, I'm all ears

Because a VisualTreeAsset is serializable you can use what's called a "direct reference" in Unity:

  • Prefix a VisualTreeAsset member with [SerializeField]
  • Assign the field in the inspector.

Now Unity will include it, and any other directly referenced assets, in the build. It will automatically be loaded into memory with everything else in that scene. You won't have to use the AssetDatabase, or any other APIs only available when the UNITY_EDITOR is present. If for whatever reason you can't use a direct reference but still need a solution which works on device you can look into Unity's Resource API for something simple or Addressables for a more robust and scalable solution.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ This sounds like it would do the trick, I'll have to try it out later this week, and will accept the answer as solution after that. Already many thanks for helping out at least :) \$\endgroup\$
    – TheWolfNL
    Commented Feb 28, 2022 at 9:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .