こんにちは。
東京プログラマーのなーさんです!
ヘキサドライブに入社してから数年… 色々勉強しながら楽しくお仕事しています。
ここ最近はUnityを使用して業務をすることが多いです。
ズバリ!今回はUnityで自作クラスのInspector拡張をしよう!
GDやATなどにデータを触ってもらう際、UnityEngine.Object
を継承しない、ピュアなクラスを表示したい場合はSystem.Serializable
属性をつけたクラスをScriptableObjectやMonoBehaviourにフィールドを持たせることでInspectorに表示できますよね。
もちろん、デフォルト表示の状態で作成し、どのパラメータがどのように動くのかを資料にしたうえで担当者に渡せば十分データとしては機能します。
ですが、データの用途に合った見た目に整えて視覚的なミスを減らしたり、単純作業になりそうな部分自動化したりするのもPGの業務です。
今回はResourcesフォルダ以下に存在するAudioClipのパスを、手打ちではなくObjectFieldへDrag&Dropすることで自動で割り当てられる簡易的なEditor拡張をお伝えします!
今回作成するEditor拡張のゴールはこちら!
・検索用のキーとAudioClipのResourcesPathが1対1となるデータクラス
・ScriptableObjectで参照を持ち、ObjectFieldにDrag&DropするとResourcesPathが自動で割り当てられる
・ResourcesPathは変更されないようにしておく
早速Editor拡張のソースコードの実装!…の前に
ScriptableObjectとデータクラスの実装から。
上記のソースコードを書いてScriptableObjectを作成し、Inspectorを確認すると下記画像のようになります。
今回のEditor拡張でInspectorの挙動を実装していくクラスは「SoundResourcesParameter.cs」です!
まずはデフォルト表示と同じものを表示できるよう実装していきます。
★ポイント
・ピュアなC#のクラスをInspectorに表示させたい場合は、CustomPropertyDrawer
属性をつけたPropertyDrawer
を継承したクラスで実装する
・クラスのフィールドにアクセスしたい場合、予めSerializeField
属性をつけたうえで、SerializedProperty.FindPropertyRelative()
の引数にフィールド名と同じ文字列を入れることでアクセス可能
・OnGUI()
メソッド内で、GUILayout
及びEditorGUILayout
のメソッドを呼ぶことで描画
上記ソースコードを実装し、コンパイルが完了すると下記画像のようになります。
やったー!
あれ?何か表示がオカシイような気がする…?
みなさんは表示の違和感に気づきましたか?
正解は、折り畳みボタンが存在しないことです!
これではデフォルトの表示と全然違う…
なので修正していきます!
★ポイント
・折り畳みに関するフラグはEditorGUI.Foldout()
メソッドを使用する
・加えて、単に表示させるだけでなく、EditorGUI.IndentLevelScope()
を使用してインデントを作り、見た目を整える
これでデフォルトの表示と同じようなInspector表示になりました!
やったー!
このままEditorGUILayout
で実装を進めても良いですが、
今後さらなるInspector表示の変更を行うことを考えて、もう少し見た目の実装を整えましょう!
★ポイント
・EditorGUILayout
->EditorGUI
に変更
・GetPropertyHeight
メソッドで、プロパティ表示時の高さを定義
・EditorGUI.DisabledScope
でResourcesPathの打ち込み部分を入力できない状態にしておき、意図しない値が打ち込まれないようにしておく
いよいよAudioClipをDrag&Dropした際にResourcesPathが自動で打ち込まれる実装をしていきます!
UnityEngine.Object
のパスを取得したい場合は、AssetDatabase
クラスを使用します!
★ポイント
・UnityEngine.Object
からパスへ変換するには、一度guidへ変換してからパスに変換する
・リソースパスで読み込むために、必要がない文字列を除外することを忘れずに!
これで無事にFieldにDrag&Dropを行うことで自動でリソースパスが割り当てられるようになりました!
やったー!
実は!まだ上記のソースコードには不具合があります!!!
SoundResourcesParameterを配列で持ち、要素数が2つ以上の場合に、それぞれの要素に値を割り当てようとすると同じ参照になってしまうのです…
皆さんはソースコードから不具合となる箇所を見つけられますか?
正解はUpdateSerializedProperty()のnullチェックの部分でした!
配列内の描画で使用していたクラスは、同じ参照先を使用していたみたいです。
これで配列の場合も含めたEditor拡張が完成!
お疲れ様でした!
今回の実装はここで終わりですが、まだまだ改良の余地はありそうです。
例えば…
・変更差分が出た場合にラベルの色を変更させる
・ScriptableObjectを直接触るのではなく、専用のWindowを作る
・Addressable対応を行う
皆さんもEditor拡張をして快適なUnity環境を整えてみてはいかがでしょうか?
それでは!