Version: 2023.2
言語: 日本語
エディターウィンドウ
カスタムエディター

Property Drawer

Note: Unity エディター を拡張するには、UI Toolkit を使用することを強く推奨します。UI Toolkit は、IMGUI よりも最新の機能で、柔軟性があり、スケーラブルなソリューションを提供するからです。

Property Drawer を利用すると、スクリプトの属性を使用したり、特定の Serializable クラスの外観を操作することによって、インスペクターウィンドウ の一部の GUI をカスタマイズできます。

Property Drawer には 2 つの使い方があります。

  • Serializable クラスのすべてのインスタンスの GUI をカスタマイズします。
  • カスタムの Property Attribute を持つスクリプトメンバーの GUI をカスタマイズします。

Serializable クラスの GUI をカスタマイズ

カスタムの Serializable のクラスがある場合、それが インスペクター でどのように見えるかを制御する Property Drawer を使用できます。以下のサンプルスクリプトの Serializable クラスの Ingredient に注意して考えてみてください。(ノート: これらはエディタースクリプトではありません。Property Attribute クラスは通常のスクリプトファイルに置いてください。)

C# (例)

using System;
using UnityEngine;

enum IngredientUnit { Spoon, Cup, Bowl, Piece }

// カスタムの Serializable クラス
[Serializable]
public class Ingredient
{
    public string name;
    public int amount = 1;
    public IngredientUnit unit;
}

public class Recipe : MonoBehaviour
{
    public Ingredient potionResult;
    public Ingredient[] potionIngredients;
}

カスタムの Property Drawer を使用して、インスペクターの Ingredient クラスのすべての外観を変更することができます。インスペクターの Ingredient プロパティで、カスタム の Property Drawer が有る場合、または無い場合の外観を比較してみてください。

カスタムの Property Drawer があるインスペクターのクラス (右)、無しのクラス (左)。
カスタムの Property Drawer があるインスペクターのクラス (右)、無しのクラス (左)。

CustomPropertyDrawer 属性を使用して Serializable クラスに Property Drawer を付け、それがドローワー (drawer) である Serializable クラスの型を渡すことができます。

C# (例)

using UnityEditor;
using UnityEngine;

// IngredientDrawer
[CustomPropertyDrawer(typeof(Ingredient))]
public class IngredientDrawer : PropertyDrawer
{
    // Draw the property inside the given rect
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        // Using BeginProperty / EndProperty on the parent property means that
        // prefab override logic works on the entire property.
        EditorGUI.BeginProperty(position, label, property);

        // Draw label
        position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);

        // Don't make child fields be indented
        var indent = EditorGUI.indentLevel;
        EditorGUI.indentLevel = 0;

        // Calculate rects
        var amountRect = new Rect(position.x, position.y, 30, position.height);
        var unitRect = new Rect(position.x + 35, position.y, 50, position.height);
        var nameRect = new Rect(position.x + 90, position.y, position.width - 90, position.height);

        // Draw fields - pass GUIContent.none to each so they are drawn without labels
        EditorGUI.PropertyField(amountRect, property.FindPropertyRelative("amount"), GUIContent.none);
        EditorGUI.PropertyField(unitRect, property.FindPropertyRelative("unit"), GUIContent.none);
        EditorGUI.PropertyField(nameRect, property.FindPropertyRelative("name"), GUIContent.none);

        // Set indent back to what it was
        EditorGUI.indentLevel = indent;

        EditorGUI.EndProperty();
    }
}

Property Attribute を使用したスクリプトメンバーの GUI をカスタマイズ

Property Drawer の他の使い道は、カスタムの Property Attributes を持っているスクリプト内のメンバーの外観を変更することです。例えば、スクリプトの float 型や int 型を特定の範囲に制限し、インスペクター のスライダーとしてそれらを表示したいとします。RangeAttribute と呼ばれるビルトインの PropertyAttribute を使って、以下のように行うことができます。

C# (例)

// float をインスペクター内で範囲が 0 から 10 のスライダーとして表示します
[Range(0f, 10f)]
float myFloat = 0f;

同様ににて、独自の PropertyAttribute を作ることができます。例として RangeAttribute のコードを使用します。属性は PropertyAttribute クラスを拡張する必要があります。好みに応じて、プロパティはパラメーターを取り、パブリックのメンバー変数としてそれらを格納できます。

C# (例)

using UnityEngine;

public class MyRangeAttribute : PropertyAttribute 
{
        readonly float min;
        readonly float max;
        
        void MyRangeAttribute(float min, float max)
        {
            this.min = min;
            this.max = max;
        }
}

属性を作成したので、その属性を持つプロパティを描く Property Drawer を作成する必要があります。ドローワーは PropertyDrawer クラスを拡張する必要があり、どの属性のドローワーであるかを伝えるために CustomPropertyDrawer 属性を持つ必要があります。

Property Drawer クラスは Editor と呼ばれるフォルダー内のエディタースクリプトに置きます。

C# (例)

using UnityEditor;
using UnityEngine;

// MyRangeDrawer に、それが MyRangeAttribute を持つプロパティのドローワーであることを伝えます
[CustomPropertyDrawer(typeof(MyRangeAttribute))]
public class RangeDrawer : PropertyDrawer
{
    // 指定した矩形内にプロパティを描画
    void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        // スライダーの範囲情報を持っているので、最初に属性を取得します
        MyRangeAttribute range = (MyRangeAttribute)attribute;

        // プロパティが float か int かによって、Slider または IntSlider を描画します
        if (property.propertyType == SerializedPropertyType.Float)
            EditorGUI.Slider(position, property, range.min, range.max, label);
        else if (property.propertyType == SerializedPropertyType.Integer)
            EditorGUI.IntSlider(position, property, (int) range.min, (int) range.max, label);
        else
            EditorGUI.LabelField(position, label.text, "Use MyRange with float or int.");
    }
}

パフォーマンス上の理由から、EditorGUILayout 関数を Property Drawer では使用できないことを注意してください。

エディターウィンドウ
カスタムエディター