Manual: Bind to nested properties (original) (raw)
Create a binding with the Inspector
Version: 2021.3+
This example demonstrates how to use the binding-path
attribute of a BindableElement in UXML to bind fields to nested properties of a SerializedObject.
Example overview
This example creates a custom InspectorA Unity window that displays information about the currently selected GameObject, asset or project settings, allowing you to inspect and edit the values. More info
See in Glossary UI with the following:
- Two fields that bind to a GameObjectThe fundamental object in Unity scenes, which can represent characters, props, scenery, cameras, waypoints, and more. A GameObject’s functionality is defined by the Components attached to it. More info
See in Glossary’s name and the scale of a USS transform - Two fields that bind to nested properties of a SerializedObject
A custom Inspector for a script, which has fields that map to GameObjects and SerializedObjects.
You can find the completed files that this example creates in this GitHub repository.
Prerequisites
This guide is for developers familiar with the Unity Editor, UI Toolkit, and C# scripting. Before you start, get familiar with the following:
- Visual TreeAn object graph, made of lightweight nodes, that holds all the elements in a window or panel. It defines every UI you build with the UI Toolkit.
See in Glossary - UXML
- USS
- BindableElement
- PropertyField
Create the destructible tank object
Create a C# script to define a class for a tank that has health values to make it destructible.
- Create a project in Unity with any template.
- In your Project windowA window that shows the contents of your
Assets
folder (Project tab) More info
See in Glossary, create a folder namedbind-nested-properties
to store all the files. - Create a C# script named
DestructibleTankScript.cs
and replace its content with the following:
using System;
using UnityEngine;
using UnityEngine.Serialization;
[Serializable]
public struct Health
{
public int armor;
public int life;
}
[ExecuteInEditMode]
public class DestructibleTankScript : MonoBehaviour
{
public string tankName = "Tank";
public float tankSize = 1;
public Health health;
private void Update()
{
gameObject.name = tankName;
gameObject.transform.localScale = new Vector3(tankSize, tankSize, tankSize);
}
public void Reset()
{
health.armor = 100;
health.life = 10;
}
}
Create the UXML and the Inspector UI
Create a UXML file with a BindableElement. Set the BindableElement’s binding-path
to the health
property and set each child element’s binding-path
of the BindableElement to the armor
and life
properties of health
.
- In the bind-nested-properties folder, create a folder named
Editor
. - In the Editor folder, create a USS file named
tank_inspector_styles.uss
and replace its contents with the following:
.container {
background-color: rgb(80, 80, 80);
flex-direction: column;
}
Label {
background-color: rgb(80, 80, 80);
}
TextField:hover {
background-color: rgb(255, 255, 0);
}
FloatField {
background-color: rgb(0, 0, 255);
}
- Create a UI Document named
destructible_tank_editor.uxml
and replace its contents with the following:
<UXML xmlns="UnityEngine.UIElements" xmlns:ue="UnityEditor.UIElements">
<Style src="tank_inspector_styles.uss"/>
<VisualElement name="row" class="container">
<Label text="Tank Script - Custom Inspector" />
<ue:PropertyField binding-path="tankName" name="tank-name-field" />
<ue:PropertyField binding-path="tankSize" name="tank-size-field" />
<BindableElement binding-path="health">
<ue:PropertyField binding-path="armor"/>
<ue:PropertyField binding-path="life"/>
</BindableElement>
</VisualElement>
</UXML>
Create the custom Editor
Create a C# script that registers a custom Editor for the DestructibleTankScript
.
- Create a C# script named
DestructibleTankEditor.cs
and replace its content with the following:
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
[CustomEditor(typeof(DestructibleTankScript))]
public class DestructibleTankEditor : Editor
{
[SerializeField]
VisualTreeAsset visualTreeAsset;
public override VisualElement CreateInspectorGUI()
{
return visualTreeAsset.CloneTree();
}
}
- Select
DestructibleTankEditor.cs
in the Project window. - Drag
destructible_tank_editor.uxml
to Visual Tree Asset in the Inspector.
Test the binding
- In Unity, add an empty GameObject to a sceneA Scene contains the environments and menus of your game. Think of each unique Scene file as a unique level. In each Scene, you place your environments, obstacles, and decorations, essentially designing and building your game in pieces. More info
See in Glossary. - Select the GameObject.
- Add the Destructible Tank Script component in the Inspector. The Armor and Life fields are bound to the
health.armor
andhealth.life
properties. If you change the values in the Inspector, the values of the underlying properties change.
Additional resources
- SerializedObject data binding
- Bindable elements
- Binding data type conversion
- Implementation details
- Binding examples
Create a binding with the Inspector