1. SerializedObject と SerializedProperty の概要
Unity では、特定のスクリプトが Inspector に表示される方法を完全にカスタマイズすることが可能です。
SerializedObject と SerializedProperty は、Unity エディタ内でシリアライズ可能なオブジェクトのプロパティを操作するために使用されるクラスです。これらは、カスタムエディタを作成してプロパティパネルをより柔軟に制御する際に利用されます。
基本的な使い方:
SerializedObject:スクリプトオブジェクト全体を表します。SerializedProperty:そのオブジェクト内の特定のプロパティを表します。
2. カスタム Inspector の作成手順
- 対象スクリプト用のカスタムエディタスクリプトを作成し、
Editorクラスを継承します。 - カスタムエディタスクリプトに
[CustomEditor(typeof(対象クラス))]属性を追加します。
例:
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(MyScript))]
public class MyScriptEditor : Editor
{
private SerializedProperty atk;
private SerializedProperty def;
private void OnEnable()
{
atk = serializedObject.FindProperty("atk");
def = serializedObject.FindProperty("def");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.IntSlider(atk, 0, 100, new GUIContent("攻撃力"));
EditorGUILayout.FloatField(def, new GUIContent("防御力"));
serializedObject.ApplyModifiedProperties();
}
}
3. 配列やリストのカスタム表示
3.1 標準表示
EditorGUILayout.PropertyField を使用することで、配列やリストの標準的な表示が可能です。
EditorGUILayout.PropertyField(listProp, new GUIContent("リスト表示"));
3.2 カスタム表示
配列やリストの要素を個別に制御したい場合は、以下の API を使用します:
arraySize:要素数を取得InsertArrayElementAtIndex(index):指定位置に要素を挿入DeleteArrayElementAtIndex(index):指定位置の要素を削除GetArrayElementAtIndex(index):指定位置の要素を取得
public override void OnInspectorGUI()
{
serializedObject.Update();
int size = EditorGUILayout.IntField("要素数", listProp.arraySize);
if (size != listProp.arraySize)
{
while (listProp.arraySize < size)
listProp.InsertArrayElementAtIndex(listProp.arraySize);
while (listProp.arraySize > size)
listProp.DeleteArrayElementAtIndex(listProp.arraySize - 1);
}
for (int i = 0; i < listProp.arraySize; i++)
{
SerializedProperty element = listProp.GetArrayElementAtIndex(i);
EditorGUILayout.PropertyField(element, new GUIContent("要素 " + i));
}
serializedObject.ApplyModifiedProperties();
}
4. カスタムクラスの表示制御
4.1 標準表示
カスタムクラスを Inspector に表示するには、そのクラスに [Serializable] 属性を追加する必要があります。
[Serializable]
public class CustomData
{
public int value;
public string name;
}
4.2 カスタム表示
カスタムクラス内の個別プロパティにアクセスするには、FindPropertyRelative メソッドを使用します。
private SerializedProperty customValue;
private SerializedProperty customName;
private void OnEnable()
{
customValue = serializedObject.FindProperty("data.value");
customName = serializedObject.FindProperty("data.name");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
customValue.intValue = EditorGUILayout.IntField("値", customValue.intValue);
customName.stringValue = EditorGUILayout.TextField("名前", customName.stringValue);
serializedObject.ApplyModifiedProperties();
}
5. Dictionary 型のカスタム表示
Unity では Dictionary 型は直接シリアライズできません。代わりに、[SerializeField] で修飾された 2 つのリスト(キーと値)を使い、ISerializationCallbackReceiver インターフェースでシリアライズ時の処理を制御します。
public class MyScript : MonoBehaviour, ISerializationCallbackReceiver
{
public Dictionary myDict = new Dictionary();
[SerializeField] private List<int> keys = new List<int>();
[SerializeField] private List<string> values = new List<string>();
public void OnBeforeSerialize()
{
keys.Clear();
values.Clear();
foreach (var pair in myDict)
{
keys.Add(pair.Key);
values.Add(pair.Value);
}
}
public void OnAfterDeserialize()
{
myDict.Clear();
for (int i = 0; i < keys.Count; i++)
{
if (!myDict.ContainsKey(keys[i]))
myDict.Add(keys[i], values[i]);
}
}
}
エディタ拡張側では、このリストを元に Dictionary の編集UIを構築します。
public override void OnInspectorGUI()
{
serializedObject.Update();
SerializedProperty keys = serializedObject.FindProperty("keys");
SerializedProperty values = serializedObject.FindProperty("values");
int size = EditorGUILayout.IntField("辞書要素数", keys.arraySize);
while (keys.arraySize < size)
{
keys.InsertArrayElementAtIndex(keys.arraySize);
values.InsertArrayElementAtIndex(values.arraySize);
}
while (keys.arraySize > size)
{
keys.DeleteArrayElementAtIndex(keys.arraySize - 1);
values.DeleteArrayElementAtIndex(values.arraySize - 1);
}
for (int i = 0; i < size; i++)
{
EditorGUILayout.BeginHorizontal();
keys.GetArrayElementAtIndex(i).intValue = EditorGUILayout.IntField("キー", keys.GetArrayElementAtIndex(i).intValue);
values.GetArrayElementAtIndex(i).stringValue = EditorGUILayout.TextField("値", values.GetArrayElementAtIndex(i).stringValue);
EditorGUILayout.EndHorizontal();
}
serializedObject.ApplyModifiedProperties();
}