Unity 6 UIToolkit 自定义控件开发指南
Unity6のUIToolKitでカスタムコントロールを作る
译文尚不完整,完整内容请切换到原文。
(UIToolKitは)初投稿です
はじめに
最近SNSでデバッガーの開発にUIToolKitを使う記事を見かけて以来
Unity6でUIToolKitを利用してEditor拡張等々を開発しています。
今回は、
意外とUnity6になってからのカスタムコントロールの作り方の記事がなかったので
自身と同じようにUnity6で入門する人に向けて簡単に書き残しておこうと思います。
実装環境
Unity 6000.3.24f1
LTS内のバージョンアップでも結構自動生成周りのコードの出力結果が違います
お手元のUnityで実装し動作を確認しながら環境にあった実装へ変更ください
本編
共通
对于想要创建自定义控件,请让其继承任意 VisualElement。
最基本的是VisualElement
如果想利用 UIToolkit 的绑定功能来创建控件,请选择BindableElement
如果想创建文本元素控件,请选择持有TextElement属性等的text
虽然稍显例外,但在进行即时渲染时选择ImmediateModeElement
……诸如此类。
起初内容较为复杂,因此建议先直接继承VisualElement即可。
在制作各种控件的过程中加深理解后,
再逐步推进到创建自定义基类控件,或继承现有控件进行实现。
自動生成
针对直接或间接继承目标 VisualElement 的类
通过添加partial关键字定义并赋予UxmlElementAttribute,
可以将其变为可在 UI Builder 中作为元素组合使用的自定义控件。
由于这是由 SourceGenerator 自动实现的,
因此在 Unity Domain 加载完成后,可以从 Visual Studio 等工具中查看生成的代码。
它被命名为Unity.UIToolKit.SourceGenerator并集成其中
如果您希望为text等赋予简单的自定义值,可以使用UxmlAttribute,
它能在自动生成的代码中包含反映这些值的简单代码。
虽然简便,但直接实现超出自动生成范围的功能比较困难。
定义代码
[UxmlElement]
public partial class HogeControl
: VisualElement
{
[UxmlAttribute] private int value;
}
自动生成的代码
public partial class Test_Button
{
[global::System.Runtime.CompilerServices.CompilerGenerated]
[global::System.Serializable]
public new class UxmlSerializedData : global::UnityEngine.UIElements.VisualElement.UxmlSerializedData
{
[global::UnityEngine.UIElements.RegisterUxmlCacheAttribute]
[global::System.Diagnostics.Conditional("UNITY_EDITOR")]
public new static void Register()
{
global::UnityEngine.UIElements.UxmlDescriptionCache.RegisterType(typeof(UxmlSerializedData), new global::UnityEngine.UIElements.UxmlAttributeNames[]
{
new (nameof(value), "value", null, global::System.Array.Empty<string>()),
}
#if UNITY_EDITOR_ONLY_COMPILATION
, true);
#else
, false);
#endif
}
#pragma warning disable 649
[global::UnityEngine.SerializeField] int value;
[global::UnityEngine.SerializeField, global::UnityEngine.UIElements.UxmlIgnore, global::UnityEngine.HideInInspector] global::UnityEngine.UIElements.UxmlSerializedData.UxmlAttributeFlags value_UxmlAttributeFlags;
#pragma warning restore 649
public override object CreateInstance() => new Test_Button();
public override void Deserialize(object obj)
{
base.Deserialize(obj);
var e = (Test_Button)obj;
if (ShouldWriteAttributeValue(value_UxmlAttributeFlags))
{
e.value = this.value;
}
}
}
}
关于UxmlAttribute
这是一种可以附加到字段或属性上的特性,
其第一个参数可以指定在UXML中使用的名称。
这样可以避免编码风格被强制绑定到UXML的命名规范上。
また、第二引数にこれまで利用していた名前をparams形式で渡す事ができます。
あまり名前は変えるべきではありませんが、どうしても変更したい場合で
後方互換性を維持する必要のあるバージョンアップでは有効な機能です。
手写
需要手动定义UxmlSerializedData类型。
参考自动生成的代码等应该能理解基本内容,
这里只对要点简单添加注释。
using System;
using UnityEnine;
using UnityEngine.UIElements;
public class HogeControl : VisualElement
{
// NOTE: Serializableである必要がある
[Serializable]
// NOTE: VisualElement型にUxmlSerializedDataが定義されているので new で定義しつつ継承する
public new class UxmlSerializedData : VisualElement.UxmlSerializedData
{
// NOTE: 同じく new 定義し、Editorでのみ動作させるためにConditionalする
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public new static void Register()
{
// NOTE: 独自に定義したい値等を実際に登録する
UxmlDescriptionCache.RegisterType(typeof(UxmlSerializedData), Array.Empty<UxmlAttributeNames>(), true);
}
// NOTE: D&DやUXMLから生成される時に実行されるインスタンスメソッド
public override object CreateInstance() => new HogeControl();
}
}
定义独有的值
命名
我认为基本上采用不带 prefix・suffix 的命名方式会更好。
这是因为,使用 _ 的部分会在 UXML 上自动被替换为 -。
m_expand 是使用了 m-expand 的名称。
既定事项
需要为要定义的值设置UxmlAttributeFlags这个enum值。
命名必须为???_UxmlAttributeFlags,
由于这依赖于UXML的实现,因此似乎很难摆脱该规则。
using System;
using UnityEngine.UIElements;
public class HogeControl : VisualElement
{
[Serializable]
public new class UxmlSerializedData : VisualElement.UxmlSerializedData
{
// NOTE: コントロールがUXMLとして表現されるときに持つデータを定義する
[SerializeField] private bool expand;
// NOTE: 値を変更しているか等のexpandの属性データを表現するデータ
[SerializeField, UxmlIgnore, HideInInspector] private UxmlAttributeFlags expand_UxmlAttributeFlags;
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public new static void Register()
{
UxmlDescriptionCache.RegisterType(typeof(UxmlSerializedData), new UxmlAttributeNames[]
{
// NOTE: 名前指定でexpandをUXMLの要素の値として定義する
new("expand", "expand", null, null),
}, true);
}
public override object CreateInstance() => new HogeControl();
public override void Deserialize(object obj)
{
base.Deserialize(obj);
var e = (HogeControl)obj;
// NOTE: UXMLから実際のUIに変換されるときに属性を見て必要なら値を渡す
if (ShouldWriteAttributeValue(expand_UxmlAttributeFlags))
{
e.expand = this.expand;
}
}
}
// NOTE: コントロールが実際に持つデータを定義する
[SerializeField] private bool expand;
}
结尾
我本想再多写一些关于CreateProperty和INotifyValueChanged的内容,
但在边写边查阅详细规格并实现的过程中,发现了许多完全不了解的东西,
所以我会努力追加内容或在新文章中介绍……
前路漫漫~
查看点赞用户列表
0
注册新用户以更便捷地使用Qiita
- 获取符合您需求的文章
- 您可以高效地回顾有用信息
- 您可以使用深色主题
来源:Qiita · Unity 开发社区 · qiita.com