Search Unity

How to clone or create a prefab out of a complex VisualElement

Discussion in 'UI Toolkit' started by UniZoja, Oct 11, 2021.

  1. UniZoja

    UniZoja

    Joined:
    Oct 1, 2018
    Posts:
    30
    I have a complex visual element, which consists of multiple other visual elements, a scroll view and a few text labels. Here is the example:

    complex_visual_element.png

    Now I know I can save the styles of all of these elements into a stylesheet file (uss) and simply apply all the styles by calling:
    Code (CSharp):
    1. .styleSheets.Add(stats_object_uss);
    The issue I am having now is I need to manually recreate all these elements and parent them, which feels like the wrong way to do it. For example, this is how I do it for simple visual elements:

    Code (CSharp):
    1. UnityEngine.UIElements.Button floorSelectBtn = new UnityEngine.UIElements.Button();
    2. floorSelectBtn.AddToClassList("Floor_btn");
    3. floorSelectBtn.styleSheets.Add(game_object_uss);
    4. floorSelectBtn.text = (string)elem["floor"];
    5. floorSelectBtn.clicked += delegate { ShowFloor((int)elem["floor"]); };
    There must be a fast way to get the whole childelements of #Stats_object at once, for exampe by saving this as a prefab somehow or I could leave one dummy element and make clones.

    Thanks in advance
     
  2. JuliaP_Unity

    JuliaP_Unity

    Unity Technologies

    Joined:
    Mar 26, 2020
    Posts:
    700
  3. UniZoja

    UniZoja

    Joined:
    Oct 1, 2018
    Posts:
    30
    Thanks for the reply, so basically the UXML is the prefab. As a rule of thumb one can say if I start to repeat myself and use copy the same Visual Element, I should create it as a seperate UXML.

    I can also parent different UXML's? Because they also have their own panel settings with a sort order.
     
  4. JuliaP_Unity

    JuliaP_Unity

    Unity Technologies

    Joined:
    Mar 26, 2020
    Posts:
    700
    Exactly! That way it can be reused over and over :cool:

    Not entirely sure what you mean here though. You can add a full UXML as a child of any VisualElement, be it one you create by code or instantiated from a different UXML.

    Be aware, though, that if you use multiple PanelSettings, you may impact performance. Ideally, if you have the same resolution configuration you should share a PanelSettings and order your layers inside it, either by multiple UIDocuments or even within the C# code by bringing different elements forward/backwards.
     
  5. UniZoja

    UniZoja

    Joined:
    Oct 1, 2018
    Posts:
    30
    Thanks a lot for the clarification. I have now a much better understanding on how it is intended to use the UXML's.

    True, I can simply use the same panel settings! Thanks again.
     
    JuliaP_Unity likes this.
  6. UniZoja

    UniZoja

    Joined:
    Oct 1, 2018
    Posts:
    30
    Hello again. Found some time to continue on my game again.

    In theory I understand how it should work, but I am still stuck. After many different attempts I feel like I am close now, but I am stuck on an error, which does not really help me:

    Code (CSharp):
    1. ArgumentNullException: Value cannot be null.
    2. Parameter name: e
    So to sum it up, here is my main UI. You see, there is a scrollview with "Stats_object". In future I want to put multiple Stats_objects there:

    q1.png

    Now what I did is: I created a new UXML file, which shares the same panel settings as my main GUI and replicated this element there. I had to create a new empty object and add the UI Document. I also had to deactive the object, because its just a template/dummy:

    2.png

    Now in the last step I have created a SerializeField in my UI controller script and saved the reference there:
    Code (CSharp):
    1. [SerializeField] UIDocument statsObject
    3.png

    Now I was able to make Instances of this UI Document. But the code crashes with the error above. Am I even doing it correctly? I hope my example is understandable.

    Here is the code:

    Code (CSharp):
    1. int inserts = 0;
    2. while (inserts != 3) {
    3.     UIDocument statsObjectNew = Instantiate(statsObject);
    4.     statsObjectNew.enabled = true;
    5.     VisualElement statsObjectC = statsObjectNew.rootVisualElement.Q<VisualElement>("StatsObject");
    6.     //statsObjectC.styleSheets.Add(game_object_uss);
    7.     UnityEngine.UIElements.Label statsObjectTitle = statsObjectC.Q<UnityEngine.UIElements.Label>("Stats_object_title");
    8.     statsObjectTitle.text = "Floor: " + (string)elem["floor"];
    9.     stats.Insert(0, statsObjectC);
    10.     inserts += 1;
    11.     Debug.Log("inserted");
    12. }

    EDIT:

    Fixed it, I had to make a real prefab out of it. The error was caused by it beeing disabled. Now everything works. I leave the question as is for others or maybe there is better way to do it!
     
    Last edited: Oct 19, 2021
  7. JuliaP_Unity

    JuliaP_Unity

    Unity Technologies

    Joined:
    Mar 26, 2020
    Posts:
    700
    I'm not sure exactly from what line that error is coming from, without a full stack trace it's very tricky. I see that you fixed it by having it as a prefab, and not an instance on your scene, and I'm glad you're not blocked anymore.

    However, I invite you to think of solving this slightly differently. Especially because by each copy being their own UIDocument, that means they'll each take the whole screen, when I suspect you may want these copies to be part of your existing UI and not standalone parts of the UI. Also, by having your UXML file you can instantiate that and not a UIDocument, and then add that to the visual tree hierarchy wherever you want to add it to.

    If this is a path you'd like to pursue, what you'll need to do is load the UXML asset (through Resources.Load if they're in the Resources folder, or by having a SerializeField of type VisualTreeAsset - instead of UIDocument) and with that call Instantiate:

    Code (CSharp):
    1. var statsObjectCopy = statsObjectVisualTreeAsset.Instantiate();
    And now you can query for the fields you want to modify on statsObjectCopy and add it to your visual tree :cool:
     
    Deeje, dlorre and UniZoja like this.
  8. UniZoja

    UniZoja

    Joined:
    Oct 1, 2018
    Posts:
    30
    Thanks a lot!