自己紹介
最近Unity関係で頼られることが多くなって嬉しい大学生。
Xもフォローしてね。
今回の記事について
今回は、IMGUIを使ったことがない人向けにUI Toolkitで実装できるPropertyDrawerを実装できるようにする記事です。
私もIMGUIを触ったことがない状態で、UI Toolkitで実装しました。
なにつくったの?
今回は、SerializedReference
を選択できるSubClassSelector
AttributeをUI Toolkitで実装していきます。
SubClassSelector
はインターフェースをシリアライズできる
元ネタ様
CustomPropertyDrawerとは?
CustomPropertyDrawer
とは、Unityのインスペクターでの挙動を変えられるようになるエディター拡張です。
PropertyDrawer
クラスを継承して実装していきます。
public class SubclassSelectorPropertyDrawer : PropertyDrawer
{
// 処理を書いていく
}
UI Toolkitを用いたPropertyDrawer
UI Toolkitを用いて制作する場合はCreatePropertyGUI()
を使います。
public class SubclassSelectorPropertyDrawer : PropertyDrawer
{
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
// VisualElementをreturnするとそれがそのインスペクターになる。
}
}
PropertyDrawerを実装する注意点まとめ
1.保存されない問題
ただ、値を変更しても保存されません。
AssetDatabase.SaveAssets();を使用すれば保存されます。
property.managedReferenceValue = Activator.CreateInstance(type);
property.serializedObject.ApplyModifiedProperties();
AssetDatabase.SaveAssets();
2.タイプを取得したい
継承したPropertyDrawer
クラスにfieldInfo
という変数が存在し、それから取得する方法が出来ます。
3.Attributeの実装に使うクラス
Attributeの実装に使うクラスはAttribute
クラスではなくPropertyAttribute
クラスのようです。
UI Toolkitで実装する際の注意点まとめ
1.幅が合わない問題
問題の概要
下記の画像の下の部分が今回実装したものなのですが、幅がズレています。
これを修正します。
修正方法
以下のようにUSSを適用してあげると良いようです。
var element = new VisualElement();
// 何かの処理
element.AddToClassList(BaseField<object>.alignedFieldUssClassName);
// 何かの処理
return element;
BaseField<T>
のTの部分はなんでもよいようです。
特に大きく処理が変わらなかったため、Object
としています。
誰か違いを知っている人がいれば教えてください。
今回作ったソースコード
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
[CustomPropertyDrawer(typeof(SubClassSelectorAttribute))]
public class SubclassSelectorPropertyDrawer : PropertyDrawer
{
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
var dropdown = new DropdownField(property.name);
dropdown.AddToClassList(BaseField<object>.alignedFieldUssClassName);
// ドロップダウンの要素を指定
var types = GetAllInterface().ToList();
dropdown.choices = types.Select(x => x.Name).ToList();
dropdown.value = types.First(x => x.Name == property.managedReferenceValue.GetType().Name).Name;
dropdown.RegisterValueChangedCallback(e =>
{
var type = types.First(x => e.newValue == x.Name);
property.managedReferenceValue = Activator.CreateInstance(type);
property.serializedObject.ApplyModifiedProperties();
AssetDatabase.SaveAssets();
});
return dropdown;
}
private IEnumerable<Type> GetAllInterface()
{
// Interfaceの型を取得する
var interfaceType = fieldInfo.FieldType.FullName;
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
// インターフェースを継承しているクラスの一覧を取得する。
foreach (var assembly in assemblies)
{
var types = assembly.GetTypes()
.Where(t => t.GetInterface(interfaceType) is not null && t.IsClass);
foreach (var type in types)
{
yield return type;
}
}
}
}
using System;
using UnityEngine;
[AttributeUsage(AttributeTargets.Field)]
public class SubClassSelectorAttribute : PropertyAttribute
{
}
最後に
元ネタ様からすると機能不足ですが、今回使用したいものとしては十分でした。
元ネタ様はUI Toolkitを使っていなかったので、記事にしてみました。