はじめに
みなさんエディタ拡張していますか?
私はエディタ拡張という名前からかなり高度な知識が必要では?と初めは思っていたのですが、実際に触ってみると意外とシンプルだったと感じました。
この記事ではインスペクターのエディタ拡張を扱い、以下の画像のよう見た目・操作性などのメリットをもたらす事ができます。

特にScriptableObject
というデータを扱うアセットのインスペクターをエディタ拡張するとデータ管理が圧倒的に楽になりオススメですね。
こういったインスペクター拡張の手順を紹介していこうと思います。
またここではUIElements
ではなく昔ながらのIMGUI
でやっていくのであしからず。
対象について
まずインスペクターのエディタ拡張をするにあたって対象のオブジェクトが必要になります。
というわけで以下の2つのクラスを用意しました。
public class MonoBehaviourModel : MonoBehaviour { public int publicValue; [SerializeField] private int serializedValue; public void Log() { Debug.Log($"publicValue: {publicValue}"); Debug.Log($"serializedValue: {serializedValue}"); } }
[CreateAssetMenu()] public class ScriptableObjectModel : ScriptableObject { public int publicValue; [SerializeField] private int serializedValue; public void Log() { Debug.Log($"publicValue: {publicValue}"); Debug.Log($"serializedValue: {serializedValue}"); } }
MonoBehaviour
を継承しているクラスはゲームオブジェクトにアタッチすることでインスタンス化され,ScriptableObject
を継承しているクラスはアセットを作成することでインスタンス化されます。
それぞれのインスペクターはこんな感じ。


手順
1. Editorフォルダを作成する
エディタ拡張関連のスクリプトはEditor
という名前のフォルダの中にいれないと動作しません。
Editor
フォルダはルートになければいけないという制約があるわけではないので、好きな場所に作ってみてください。
次から書くスクリプトは全部この中に入れます。
2. Editorクラスを継承,CustomEditor属性をつける
早速先ほど作成したオブジェクトのインスペクター拡張を開始しましょう。
まずはEditor
クラスを継承したクラスを作成します。
UnityEditor.Editor - Unity スクリプトリファレンス
このときにインスペクター拡張したい対象のクラスをCustomEditor
属性で指定してあげてください。
カスタムエディター - Unity マニュアル
using UnityEditor; [CustomEditor(typeof(MonoBehaviourModel))] public class MonoBehaviourModelEditor : Editor { }
3. OnInspectorGUIを実装する
次にOnInspectorGUI
メソッドを作成します。
これはEditor
クラスにvirtual
なメソッドとして定義されていて、Unityがインスペクター上でエディターを表示するたびに実行されます。
public virtual void OnInspectorGUI ();
Editor-OnInspectorGUI - Unity スクリプトリファレンス
using UnityEditor; [CustomEditor(typeof(MonoBehaviourModel))] public class MonoBehaviourModelEditor : Editor { public override void OnInspectorGUI() { } }
4. 値を取得・表示する
SerializedObjectを使ったやり方
次に拡張の対象のクラスで定義されていたフィールドの値を取得・更新してみます。
実は対象オブジェクトとのやり取りは2種類の方法がありますが、最初はSerializedObject
を通したやり方の説明をします。
フィールドの取得にはSerializedObject.FindProperty
,
SerializedObject-FindProperty - Unity スクリプトリファレンス
EditorGUILayout-PropertyField - Unity スクリプトリファレンス
using UnityEngine; using UnityEditor; [CustomEditor(typeof(MonoBehaviourModel))] public class MonoBehaviourModelEditor : Editor { public override void OnInspectorGUI() { serializedObject.Update(); // フィールドを取得する var publicValue = serializedObject.FindProperty("publicValue"); var serializedValue = serializedObject.FindProperty("serializedValue"); // インスペクターに表示する EditorGUILayout.PropertyField(publicValue); EditorGUILayout.PropertyField(serializedValue); serializedObject.ApplyModifiedProperties(); } }
またFindProperty
メソッドの返り値はSerializedProperty
という型なので,中身の値を取り出すのにはひと工夫が必要になります。
int value1 = publicValue.intValue; int value2 = serializedValue.intValue;
int
型の場合はintValue
ですが、型によって取り出し方が異なります。
詳細はSerializedProperty
のドキュメントをご覧ください。
UnityEditor.SerializedProperty - Unity スクリプトリファレンス
さらにこの手法を用いたときは以下の手順でSerializeObject
の更新が必要になります。
OnInspectorGUI
メソッドの先頭にserializedObject.Update();
,最後にserializedObject.ApplyModifiedProperties();
を書きます。
SerializedObject-Update - Unity スクリプトリファレンス
SerializedObject-ApplyModifiedProperties - Unity スクリプトリファレンス
これをすることでUnityが内部でキャッシュしている値を取得,更新することができるので、ウィンドウを複数開いていじるなんてことも可能になります。
using UnityEditor; [CustomEditor(typeof(MonoBehaviourModel))] public class MonoBehaviourModelEditor : Editor { public override void OnInspectorGUI() { serializedObject.Update(); // プロパティの取得・表示など serializedObject.ApplyModifiedProperties(); } }
対象オブジェクトに直接使うやり方
次にSerializedObject
を使わないやり方を紹介します。
こちらの場合にはEditor
クラスのプロパティからキャストしてあげることで,対象のインスタンスを取得することができます。
using UnityEditor; [CustomEditor(typeof(MonoBehaviourModel))] public class MonoBehaviourModelEditor : Editor { private MonoBehaviourModel _target; private void OnEnable() { // targetがObject型なのでキャストが必要 _target = (MonoBehaviourModel)target; } public override void OnInspectorGUI() { // 値を取得 var publicValue = _target.publicValue; // SerializedObjectを使わないやり方だとprivateなフィールドにはアクセスできない // var serializedValue = _target.serializedValue; // インスペクターに表示 publicValue = EditorGUILayout.IntField("Public Value", publicValue); } }
この方法はstring
で指定がないところが魅力的ですが、自分でデータを保存する処理を書かなければなりません。
// ScriptabelObjectを継承している場合 public override void OnInspectorGUI() { EditorGUI.BeginChangeCheck(); _target.publicValue = EditorGUILayout.IntField("Public Value", _target.publicValue); if (EditorGUI.EndChangeCheck()) { Undo.RecordObject(_target, "Change Property"); EditorUtility.SetDirty(_target); } }
本来はシーン上にオブジェクトがあるときにUndo.RecordObject
,シーン上にないときはEditorUtillity.SetDirty
を呼べば動作する?みたいですが、別に2つともかいておいても不具合が発生したことはないので私はどっちも書いてしまっています。
Undo-RecordObject - Unity スクリプトリファレンス
EditorUtility-SetDirty - Unity スクリプトリファレンス
5. 機能をもりもりにする
後はGUILayout
,EditorGUI
,EditorGUILayout
あたりを使って見た目・機能をもりもりにしちゃいましょう。
こちらの記事でたくさん紹介されていたので,是非みてみてください。
hacchi-man.hatenablog.com
さいごに
これで基本的なことはOKだと思います。
あとは実際に作りながら色々と学んでみるのが良いのではないでしょうか。
こちらのサイトもすごいオススメです↓
light11.hatenadiary.com
良きエディタ拡張ライフを!