(C# / Unity)
インスペクターをカスタムする CustomPropertyDrawer

カスタム・プロパティ・ドロワーの軽便なる効用

Unity内で、このようなクラスを作ったとします。

public class MyCustomClass : MonoBehaviour //またはScriptableObject

{

public float dummy;

}

このクラスのフィールドをどこか他のコンポーネント内で次のように宣言すると、

public class HokanoComponent : MonoBehaviour

{

public MyCustomClass target;

}

インスペクタには斯様に見慣れた参照フィールドが出ることでありましょう。

このようにUnityオブジェクトを継承したクラスのフィールドは、オブジェクト参照フィールドが表示されるのがUnityのデフォルトの挙動であります。
或いはUnityオブジェクトを継承していない[Serializable]なクラスであれば、中のdummyが直接表示されることでありましょう。

いずれにしてもこの様な、デフォルトのインスペクター表示をカスタマイズしたい場合もございます。

CustomEditorを作ればいろいろ変えられる事は敢えて申すまでもないことではありますが、然しながら今回の場合に対してはCustomEditorはまさしくオーバーキルでございまして、あくまでもMyCustomClassのフィールド部分だけカスタムできれば充分なのです。


そういう場合に適役となりますUnityの便利な機能が、まさにこのCustomPropertyDrawer(カスタム・プロパティ・ドロワー)でございます。

public class MyCustomClass : ScriptableObject

{

   public float dummy;

}


// ↓ ここから下が新たに追加した部分


#if UNITY_EDITOR

[CustomPropertyDrawer(typeof(MyCustomClass), true)]

class MyCustomClassEditor : PropertyDrawer

{

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)

    {

        EditorGUILayout.LabelField("参照フィールドの代わりのラベル");

    }

}

#endif

このようにMyCustomClassのためにPropertyDrawerの継承クラスを書きますと、対象クラス/構造体のフィールド表示を任意に置き換えることが可能なのでございます。

即ち、上のようなカスタムプロパティドロワーを定義致しますことで、下の画像の如く、参照フィールドの代わりLabelFieldの文字が表示される事と相成りました。

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)

    {

        EditorGUILayout.PropertyField(property);

        if (GUILayout.Button("dummyを123に"))

        {

            var c = property.objectReferenceValue as MyCustomClass;

            c.dummy = 123;

        }

    }

次に、プロパティ ドロワーのOnGUI()の中身を上の様に書き換えてみた結果が下の画像でございます。

(※ 表示上分かりやすくするために、MyCustomClassをHokanoComponentと同じゲームオブジェクトにアタッチしてあります)

このように、デフォルトと同じオブジェクト参照フィールドを表示させ、加えてその下にボタンを追加したものでありまして、
もちろんこの『dummyを123に』ボタンを押すことで、MyCustomClassのdummyフィールドの値は123に上書きされることは言うまでもありますまい。

世に"猫の手も杓子は借りたい"などと申しますが(※申しません)、この様に、自前のクラスにインスペクター上で特別な表示をさせたいときにはこのPropertyDrawerを活用されると、痒い所に手が届くことも大いにございましょう。


――と、さて、斯様なPropertyDrawerの軽便なる効用につきましては、その実、既に他のサイト様でより包括的に、分かりやすく述べられてきたことでありましょう。

ではなぜ改めてこのページで紹介するのかと申しますと、これに纏わる些事についても、自身への忘備録も兼ねて書き記したいからでございます。

そんなわけで、以下、プロパティドロワー周辺の諸々について書き記すものであります。

謎の空白を成敗せんとす

これはまさに私が詰まった箇所、すなわちこの項全体を書き記す直接の動機と相成った問題でもございまして、先ほどの画像をよくご覧になりましたならば、Target参照フィールドの上に、謎の空白ができていることがお分かりいただけることと思います。

先ほどのコードを見ましても、ハテ、空白を追加するようなコードは何一つ書いておりません。
でありますのに、このような結果になるとはまことに不届き千万、しかもこれは特定の条件で勝手に現れるので、たかが空白、されど空白、目の上の瘤(こぶ)のごとき障礙を感ぜられることでしょう。

さて、斯様な空白が現れる理由について深堀りすることは(その手間の軽重を考えますれば)憚られたのですが、幸いにもUnityフォオラムにてこの問題の解決法を見つけ出した御方がいらっしゃいました(*1)ものですから、自分への忘備録も兼ねて、このペヱジにて紹介しようと考えたわけでございます。

それは次のような行を、カスタムプロパティドロワークラスの中に追加するというものです。

public override float GetPropertyHeight(SerializedProperty property, GUIContent label) => 0;

従いまして、カスタムプロパティドロワー全体としましては最終的に次のようになります。

#if UNITY_EDITOR

[CustomPropertyDrawer(typeof(MyCustomClass), true)]

class MyCustomClassEditor : PropertyDrawer

{

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)

    {

        EditorGUILayout.PropertyField(property);

        if (GUILayout.Button("dummyを123に"))

        {

            var c = property.objectReferenceValue as MyCustomClass;

            c.dummy = 123;

        }

    }


    // インスペクタでドロワーの上に勝手に追加される謎の空白を消す

    public override float GetPropertyHeight(SerializedProperty property, GUIContent label) => 0;

}

#endif

これによって、見事謎の空白は消えました!

所有しているゲームオブジェクトを取得す

ところで、PropertyDrawer内で、MyCustomClassフィールドを宣言しているクラス(上の例で申しますならHokanoComponent)のインスタンスを取得するには如何すれば良いものでしょうか。

結論から申し上げますと、以下のようにすることで充分に可能で御座います。

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)

    {

//MyCustomClassのフィールドを宣言している親コンポーネントを得る

        var owner = property.serializedObject.targetObject as HokanoComponent;

    }

OnGUI()の引数として渡される[SerializeProperty property]はこの例でいうMyCustomClassのフィールドであり、SerializeProperty.serializedObject.targetObjectは即ちそのフィールドの所有者HokanoComponentでありますから、あとは型を変換せしむることで如何様にもできるのです。