版本:2021.3+
此示例演示了如何将自定义控件绑定到自定义数据类型。
此示例基于三个内置控件创建了自定义数据类型和自定义控件。它将自定义控件绑定到自定义数据类型。绘制器 (drawer) 可在摄氏度和华氏度之间转换。
您可以在此 GitHub 代码仓库中找到此示例创建的完整文件。
本指南适用于熟悉 Unity 编辑器、UI 工具包和 C# 脚本的开发者。在开始之前,请熟悉以下内容:
创建自定义数据类型 Temperature,并将其用作序列化属性。
使用任何模板创建 Unity 项目。
创建名为 bind-custom-data-type 的文件夹来存储所有文件。
创建一个名为 Temperature.cs 的 C# 脚本,并将其内容替换为以下代码:
using System;
namespace UIToolkitExamples
{
public enum TemperatureUnit
{
Celsius,
Farenheit
}
[Serializable]
public struct Temperature
{
public double value;
public TemperatureUnit unit;
}
}
创建一个名为 PlanetScript.cs 的 C# 脚本,并将其内容替换为以下代码:
using UnityEngine;
namespace UIToolkitExamples
{
public class PlanetScript : MonoBehaviour
{
public Temperature coreTemperature;
}
}
为 Planet 创建自定义编辑器,为 Temperature 创建自定义属性绘制器 (Property Drawer)。
在自定义属性绘制器中,通过写入 SerializedProperty 的属性(使用 doubleValue 和 enumValueIndex),然后调用 SerializedObject.ApplyModifiedProperties(),实现在华氏温度和摄氏温度之间转换温度的按钮。
自定义属性 Draweris 被视为自定义控件。这是一个以自定义方式行事的内置控件。
创建一个名为 Editor 的文件夹。
在 Editor 文件夹中创建一个名为 PlanetEditor.cs 的 C# 脚本,并将其内容替换为以下代码:
using UnityEditor;
using UnityEngine.UIElements;
using UnityEditor.UIElements;
namespace UIToolkitExamples
{
[CustomEditor(typeof(PlanetScript))]
public class PlanetEditor : Editor
{
public override VisualElement CreateInspectorGUI()
{
return new PropertyField(serializedObject.FindProperty("coreTemperature"));
}
}
}
在 Editor 文件夹中创建一个名为 TemperatureDrawer.cs 的 C# 脚本,并将其内容替换为以下代码:
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
namespace UIToolkitExamples
{
[CustomPropertyDrawer(typeof(Temperature))]
public class TemperatureDrawer : PropertyDrawer
{
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
var asset = Resources.Load<VisualTreeAsset>("temperature_drawer");
var drawer = asset.Instantiate(property.propertyPath);
drawer.Q<Label>().text = property.displayName;
// Don't allow conversion when you've selected multiple objects in the Inspector
if (!property.serializedObject.isEditingMultipleObjects)
{
drawer.Q<Button>().RegisterCallback<ClickEvent, SerializedProperty>(Convert, property);
}
return drawer;
}
static void Convert(ClickEvent evt, SerializedProperty property)
{
var valueProperty = property.FindPropertyRelative("value");
var unitProperty = property.FindPropertyRelative("unit");
// F -> C
if (unitProperty.enumValueIndex == (int)TemperatureUnit.Farenheit)
{
valueProperty.doubleValue -= 32;
valueProperty.doubleValue *= 5.0d / 9.0d;
unitProperty.enumValueIndex = (int)TemperatureUnit.Celsius;
}
else // C -> F
{
valueProperty.doubleValue *= 9.0d / 5.0d;
valueProperty.doubleValue += 32;
unitProperty.enumValueIndex = (int)TemperatureUnit.Farenheit;
}
// Important: Because you are bypassing the binding system, you must save the modified SerializedObject
property.serializedObject.ApplyModifiedProperties();
}
}
}
创建具有以下元素的 UXML 文件:
将两个字段的 binding-path 设置为 Temperature 属性的 value 和 unit。
在 Editor 文件夹中,创建一个名为 Resources 的文件夹。
在 Resources 文件夹中,创建一个名为 temperature_drawer.uxml 的__ UI__(即用户界面,User Interface)让用户能够与您的应用程序进行交互。Unity 目前支持三种 UI 系统。更多信息
See in Glossary 文档,并将其内容替换为以下内容:
<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">
<ui:VisualElement class="unity-base-field">
<ui:Label class="unity-base-field__label" />
<ui:VisualElement class="unity-base-field__input" style="flex-direction: row;">
<uie:DoubleField binding-path="value" />
<uie:EnumField binding-path="unit" />
<ui:Button text="Convert" />
</ui:VisualElement>
</ui:VisualElement>
</ui:UXML>
Temperature 属性会更改。