Search Unity

What is the best way to reference uxml,uss from c# script?

Discussion in 'UI Toolkit' started by RamType0, Aug 18, 2020.

  1. RamType0

    RamType0

    Joined:
    Sep 11, 2018
    Posts:
    67
    I have seen many samples,and they used AssetDatabase.LoadAssetAtPath.
    But this kind of reference is too much fragile for renaming or moving asset.
    Using GUID seems to be robust,but too much complicated.Is there any good way?
     
  2. JuliaP_Unity

    JuliaP_Unity

    Unity Technologies

    Joined:
    Mar 26, 2020
    Posts:
    698
    Hello, if you mean a way to refer to UXML and USS files from MonoBehaviour or ScriptableObject types, you can use VisualTreeAsset and StyleSheet, respectively.

    If that's not your question, can you clarify what exactly you're trying to do? :)
     
  3. RamType0

    RamType0

    Joined:
    Sep 11, 2018
    Posts:
    67
    I'm just trying to create UI with UI Toolkit.
    How we could assign SerializeField of Editor or EditorWindow?
     
  4. JuliaP_Unity

    JuliaP_Unity

    Unity Technologies

    Joined:
    Mar 26, 2020
    Posts:
    698
    Not sure I understand what you mean.
    VisualTreeAsset and StyleSheet are types you can use for serialization on both MonoBehaviour and ScriptableObject types.

    Screen Shot 2020-08-18 at 21.00.42.png Screen Shot 2020-08-18 at 21.01.01.png
     
  5. RamType0

    RamType0

    Joined:
    Sep 11, 2018
    Posts:
    67
    Yeah,we could assign any UnityEngine.Object to SerializeField of MonoBehaviour,or ScriptableObject from Inspector.
    But where could we assign object to SerializeField of UnityEditor.Editor or UnityEditor.EditorWindow?
    And here's built-in UIToolikit EditorWindow Templete,what unity officialy proposed.
    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3. using UnityEngine.UIElements;
    4. using UnityEditor.UIElements;
    5.  
    6.  
    7. public class UIToolkitEditorWindow : EditorWindow
    8. {
    9.     [MenuItem("Window/Project/UIToolkitEditorWindow")]
    10.     public static void ShowExample()
    11.     {
    12.         UIToolkitEditorWindow wnd = GetWindow<UIToolkitEditorWindow>();
    13.         wnd.titleContent = new GUIContent("UIToolkitEditorWindow");
    14.     }
    15.  
    16.     public void OnEnable()
    17.     {
    18.         // Each editor window contains a root VisualElement object
    19.         VisualElement root = rootVisualElement;
    20.  
    21.         // VisualElements objects can contain other VisualElement following a tree hierarchy.
    22.         VisualElement label = new Label("Hello World! From C#");
    23.         root.Add(label);
    24.  
    25.         // Import UXML
    26.         var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/Main/UIToolkitEditorWindow.uxml");
    27.         VisualElement labelFromUXML = visualTree.Instantiate();
    28.         root.Add(labelFromUXML);
    29.  
    30.         // A stylesheet can be added to a VisualElement.
    31.         // The style will be applied to the VisualElement and all of its children.
    32.         var styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/Main/UIToolkitEditorWindow.uss");
    33.         VisualElement labelWithStyle = new Label("Hello World! With Style");
    34.         labelWithStyle.styleSheets.Add(styleSheet);
    35.         root.Add(labelWithStyle);
    36.     }
    37. }
    This code is too much fragile for renaming or moving asset.
     
  6. JuliaP_Unity

    JuliaP_Unity

    Unity Technologies

    Joined:
    Mar 26, 2020
    Posts:
    698
    That's how assets have been referenced even before UI Toolkit, through Resources.Load as well, they all depend on hard coded paths. Even if you make references through serialized properties on MonoBehaviour or ScriptableObject, unless they're preloaded in the scene you'd have to load those assets probably by path anyway. That's why I'm not sure what the issue is :confused:
     
  7. antoine-unity

    antoine-unity

    Unity Technologies

    Joined:
    Sep 10, 2015
    Posts:
    780
    If you declare [SerializedField] on members of an EditorWindow subclass, you can also use the "Default References" when selecting the Script of the window itself.

    Our package samples use this:


    As for the default generated code from the "Create" menu, it's true that using path strings makes it harder to move files around but it makes this example self-contained and very clear (while Default References rely on the .meta file).
     
    Wanderer13 likes this.
  8. RamType0

    RamType0

    Joined:
    Sep 11, 2018
    Posts:
    67
    Thanks! It works very well! I hope this solution documented.

    But does it work for precompiled assembly?

    Default generated code is very clear example,but I think it is not a good "default".
    Default "NewBehaviourScript" or "NewPlayableAsset" doesn't behaves like it.
     
  9. antoine-unity

    antoine-unity

    Unity Technologies

    Joined:
    Sep 10, 2015
    Posts:
    780
    I don't think this will work with precompiled assemblies because this information is usually saved in meta file by Unity.

    Using a Resources folder like @JuliaP_Unity mentioned should allow a more resilient link between code and assets (only the dependency on the name remains).

    I will pass along the feedback of "not a good default" to the team.
     
    RamType0 likes this.
  10. RamType0

    RamType0

    Joined:
    Sep 11, 2018
    Posts:
    67
    I am facing with a issue with your solution.
    Default Reference could not work in PlayMode.
    It throws NullReferenceException if I opened a inspector in PlayMode.
     
  11. CxydaInno

    CxydaInno

    Joined:
    Sep 5, 2017
    Posts:
    14
    Hey RamType0,
    I wrote myself a little helper method. This is a little more robust and you can work around the issue when moving assets around (solution). (This only works in Edit mode of course. If you need runtime support you need to handle the asset loading differently)

    In Edit mode you then just have to call:
    Code (CSharp):
    1. LoadFirstAssetByFilter<VisualTreeAsset>("AwesomeTemplate")
    2.  
    3. or
    4.  
    5. LoadFirstAssetByFilter<StyleSheet>("AwesomeStyle")
    with this solution you still have issues when changing the name ¯\_(ツ)_/¯ but at least for me it's a good compromise

    Best Daniel
     
    Last edited: Sep 10, 2020
  12. antoine-unity

    antoine-unity

    Unity Technologies

    Joined:
    Sep 10, 2015
    Posts:
    780
    I have tried going into play mode with both a custom window and a custom editor using default references and it worked in both cases.
    Which Unity version are you using? Can you tell us more about your code?
     
  13. antoine-unity

    antoine-unity

    Unity Technologies

    Joined:
    Sep 10, 2015
    Posts:
    780
    Note: I have observed that Default References will not work during playmode unless the script (e.g. the CS file holding the custom window or custom editor) is inside an Editor folder or an Asmdef marked "Editor" only.

    This is expected and consistent with the fact that Unity will not "remember" default references in a player build.

    Hope this helps.
     
    egunan likes this.
  14. RamType0

    RamType0

    Joined:
    Sep 11, 2018
    Posts:
    67
    That was the reason. Thanks!
     
  15. egunan

    egunan

    Joined:
    Feb 5, 2017
    Posts:
    5
    This saved me, thanks