UIElements Tutorial for Unity: Getting Started
In this Unity tutorial, you’ll learn how to use Unity’s UIElements to create complex, flexible editor windows and tools to add to your development pipeline. By Ajay Venkat.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
UIElements Tutorial for Unity: Getting Started
35 mins
- Getting Started
- Creating an Empty Editor Window
- Understanding Visual Elements
- Exploring UXML Documents
- Analyzing a VisualElement
- Exploring the USS Document
- Exploring the Main Editor Window Controller
- Setting up the Editor Window
- Creating VisualElements Without UXML
- Attaching UXML and USS to the Editor Window
- Modifying UXML and USS Attachments
- Creating Preset Window Layouts
- Creating Layouts in UXML
- Making the Button Holder Layout
- Setting up the Main Container’s Layout
- Filling the Main Container
- Adding the Core UIElements
- Adding Functionality to the Editor Window
- Setting up the ObjectField
- Setting up Buttons
- Populating the List View
- Binding Values in UIElements
- Testing and Debugging the Editor Window
- Where to Go From Here?
Exploring the Main Editor Window Controller
In Unity’s UIElements, the UXML and USS documents are dynamic, which makes it possible to reuse them across multiple editor scripts. This allows you to keep the logic separate from the layout of the editor windows. It also means that PresetWindow.cs brings the UXML, USS documents and the logic together.
The main functions of this controller file are:
- Opening Window: Provides the control logic and method for opening the editor window.
- Starting Logic: Includes things such as linking UXML documents and applying USS documents to your VisualElements.
- Event Management: Dictates what happens when users click on buttons and interact with the editor window.
- Creating Dynamic Elements: UXML can’t create some VisualElements because they are static. You might want to create a variable number of elements, and you need logic to do this.
Open PresetWindow.cs and you should see the following code. Don’t worry if it looks overwhelming, it’s simple when you break it down.
public class PresetWindow : EditorWindow
{
// 1
[MenuItem("Window/UIElements/PresetWindow")]
public static void ShowExample()
{
// 2
PresetWindow wnd = GetWindow<PresetWindow>();
wnd.titleContent = new GUIContent("PresetWindow");
}
public void OnEnable()
{
// 3
// Each editor window contains a root VisualElement object
VisualElement root = rootVisualElement;
// 4
// VisualElements objects can contain other VisualElement following
// a tree hierarchy.
VisualElement label = new Label("Hello World! From C#");
root.Add(label);
// 5
// Import UXML
var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>
("Assets/RW/Editor/PresetWindow.uxml");
VisualElement labelFromUXML = visualTree.CloneTree();
root.Add(labelFromUXML);
// 6
// A stylesheet can be added to a VisualElement.
// The style will be applied to the VisualElement and all of its
// children.
var styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>
("Assets/RW/Editor/PresetWindow.uss");
VisualElement labelWithStyle = new Label("Hello World! With Style");
labelWithStyle.styleSheets.Add(styleSheet);
root.Add(labelWithStyle);
}
}
Copy the above code and replace the entire PresetWindow
class definition in PresetWindow.cs. Then save the file.
It isn’t any different to the boilerplate code that Unity generated earlier, except for the numbered code comments that you’ll reference in the following section, where you’ll dig into what the code does.
Setting up the Editor Window
Previously, to open the editor window you selected Window ► UIElements ► PresetWindow. The reason the menu item exists in that location is because of the menu path string set using the MenuItem attribute in the // 1
code comment section.
Change the line:
[MenuItem("Window/UIElements/PresetWindow")]
to:
[MenuItem("RW/Preset Window")]
Save the file, reopen Unity and notice the new menu created in the toolbar called RW. Now, you can open the editor window more easily by selecting RW ► Preset Window.
Next look at this code:
PresetWindow wnd = GetWindow<PresetWindow>();
wnd.titleContent = new GUIContent("PresetWindow");
Section // 2
is responsible for creating an instance of the editor window and assigning it a title.
Creating VisualElements Without UXML
When the editor window opens and becomes active, Unity calls OnEnable()
. This is where you should set up all the initial layout code, bindings and event triggers.
Look at section // 3
next:
// 3
// Each editor window contains a root VisualElement object
VisualElement root = rootVisualElement;
This code gets a reference to the rootVisualElement, which is the VisualElement at the top of the hierarchy for this editor window. As discussed before, to add elements to the editor window, you have to add VisualElements as a child to the rootVisualElement.
You’ve already seen that you can use UXML Documents to create VisualElements with structured layouts, but you can also create VisualElements dynamically.
In the section commented // 4
, a new label with some text is added:
// 4
// VisualElements objects can contain other VisualElement following
// a tree hierarchy.
VisualElement label = new Label("Hello World! From C#");
root.Add(label);
A Label
is a subclass of TextElement
, which is a subclass of VisualElement
. This is how the Label can be created as a VisualElement in this bit of code.
Once you create the label, you add it to root
using root.Add(label);
, which makes it visible in the editor window.
Attaching UXML and USS to the Editor Window
The main advantage of UXML documents is that you can use them in multiple places. Unity uses a Visual Tree, which is a hierarchy of VisualElements with parent and child relationships, to achieve this.
Each editor window has its own Visual Tree, which has a rootVisualElement at the top. A UXML document with layout information has its own self-contained Visual Tree. When you add a UXML document to an editor window, you’re merging the two Visual Trees.
Section 5 (commented // 5
) gets a reference to the UXML file and integrates its Visual Tree into the editor window’s Visual Tree:
// Import UXML
var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>
("Assets/RW/Editor/PresetWindow.uxml"); // 1
VisualElement labelFromUXML = visualTree.CloneTree(); // 2
root.Add(labelFromUXML); //3
Breaking this down:
- You create a variable that stores the Visual Tree that the PresetWindow.uxml generates.
- You clone the Visual Tree using
visualTree.CloneTree()
. The tree stores its relationships in thelabelFromUXML
variable. - You add the
labelFromUXML
to the root usingroot.Add(labelFromUXML)
. This merges the two Visual Trees.
The USS will affect the VisualElement it’s on, as well as all the children of that VisualElement.
Lastly, in code commented section // 6
:
// 6
// A stylesheet can be added to a VisualElement.
// The style will be applied to the VisualElement and all of its
// children.
var styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>
("Assets/RW/Editor/PresetWindow.uss");
VisualElement labelWithStyle = new Label("Hello World! With Style");
labelWithStyle.styleSheets.Add(styleSheet);
root.Add(labelWithStyle);
This simply creates a new label named labelWithStyle
with PresetWindow.uss attached and adds it to the root
.
Modifying UXML and USS Attachments
At the moment, the attachments of PresetWindow.uxml and PresetWindow.uss aren’t that helpful. What you want is to attach PresetWindow.uxml to the root and for PresetWindow.uss to act as a universal style sheet.
Make this change by removing all the code in OnEnable()
and replacing it with the following:
// 1
VisualElement root = rootVisualElement;
// 2
var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/RW/Editor/PresetWindow.uxml");
VisualElement uxmlRoot = visualTree.CloneTree();
root.Add(uxmlRoot);
// 3
var styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/RW/Editor/PresetWindow.uss");
var preMadeStyleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/RW/Editor/PresetTemplate.uss");
root.styleSheets.Add(styleSheet);
root.styleSheets.Add(preMadeStyleSheet);
Here’s a step-by-step breakdown of what you just did:
- Set a reference to the rootVisualElement.
- Got a reference to the Visual Tree of the PresetWindow.uxml and attached it to the
root
. - Set a reference to the style sheet from PresetWindow.uss and PresetTemplate.uss, then attached it to the
root
. These style sheets are now universal!
Save PresetWindow.cs, reload the editor window and notice the changes:
- You removed the VisualElements created within PresetWindow.cs, leaving only the label created within PresetWindow.uxml.
- The PresetWindow.uss style sheet affects the PresetWindow.uxml, as it’s now universal.