【Unity】カスタムエディターでインスペクタの見た目を変える

投稿者: | 2021-07-13

カスタムエディターを使って、インスペクタで入力する値の単位を変えてみました。

スクリプト

まず、Cubeに新しいスクリプトをつけました。このスクリプトではVector3のフィールドの値を、インスペクタで入力できるようにしています。

using UnityEngine;

public class TestCube : MonoBehaviour
{
    // インスペクタで入力
    [SerializeField] Vector3 shift;

    // Start is called before the first frame update
    void Start()
    {
        // オブジェクトの位置を取得
        var pos = transform.position;

        // 位置をずらす
        pos += shift;

        // オブジェクトの位置に適用
        transform.position = pos;

    }

    // Update is called once per frame
    void Update()
    {

    }

}

インスペクタでは、デフォルトのレイアウトが表示されています。

このレイアウトを、カスタムエディターを使って変更してみます。

カスタムエディターを作る

EditorフォルダにC#スクリプトを作りました。

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(TestCube))]
[CanEditMultipleObjects]
public class TestCubeEditor : Editor
{
    SerializedProperty shift;

    bool centimeter;
    Vector3 shiftValue;

    void OnEnable()
    {
        // SerializedPropertyを取得
        shift = serializedObject.FindProperty("shift");

        // プロパティの値を取得
        shiftValue = shift.vector3Value;
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update();

        // センチメートルのとき
        if(centimeter)
        {
            // 行を開始
            EditorGUILayout.BeginHorizontal();

            // ラベルを表示
            EditorGUILayout.LabelField("x");

            // 入力された値を取得
            shiftValue.x = EditorGUILayout.FloatField(shiftValue.x * 100f) / 100f;

            // ラベルを表示
            EditorGUILayout.LabelField("cm");

            // 行を終了
            EditorGUILayout.EndHorizontal();

            // 次の行を開始
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("y");
            shiftValue.y = EditorGUILayout.FloatField(shiftValue.y * 100f) / 100f;
            EditorGUILayout.LabelField("cm");
            EditorGUILayout.EndHorizontal();

            // 次の行を開始
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("z");
            shiftValue.z = EditorGUILayout.FloatField(shiftValue.z * 100f) / 100f;
            EditorGUILayout.LabelField("cm");
            EditorGUILayout.EndHorizontal();

        }
        // メートルのとき
        else
        {
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("x");
            shiftValue.x = EditorGUILayout.FloatField(shiftValue.x);
            EditorGUILayout.LabelField("m");
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("y");
            shiftValue.y = EditorGUILayout.FloatField(shiftValue.y);
            EditorGUILayout.LabelField("m");
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("z");
            shiftValue.z = EditorGUILayout.FloatField(shiftValue.z);
            EditorGUILayout.LabelField("m");
            EditorGUILayout.EndHorizontal();
        }

        // プロパティの値を入力値に変更
        shift.vector3Value = shiftValue;
;
        // プロパティの変更を適用
        serializedObject.ApplyModifiedProperties();

        
        if(GUILayout.Button("単位を変える"))
        {
            // キーボードフォーカスを切る
            EditorGUI.FocusTextInControl(null);

            // 切り替える
            centimeter = !centimeter;
        }

    }
}

このスクリプトでは、まずクラスにCustomEditor属性をつけて、どのコンポーネントのために動作するかを知らせます。

[CustomEditor(typeof(TestCube))]

また、このクラスにはEditorクラスを継承させます。

public class TestCubeEditor : Editor

対象のスクリプトの変数を編集するために、SerializedProperty型の変数を宣言し、オブジェクトがロードされたときに、SerializedObject.FindPropertyメソッドを使って、プロパティ名からSerializedPropertyを取得します。

SerializedProperty shift;

bool centimeter;
Vector3 shiftValue;

void OnEnable()
{
    // SerializedPropertyを取得
    shift = serializedObject.FindProperty("shift");

    // プロパティの値を取得
    shiftValue = shift.vector3Value;
}

そして、インスペクタでのレイアウトや処理をOnInspectorGUIメソッドに記述します。例えば、テキストを表示するときは、EditorGUILayout.LabelFieldメソッドを使います。

EditorGUILayout.LabelField("x");

また、EditorGUILayout.FloatFieldメソッドで入力欄を表示して値を受け取ることもできます。

shiftValue.x = EditorGUILayout.FloatField(shiftValue.x * 100f) / 100f;

この引数に渡す値が入力欄に表示されます。値が入力されるとその値が戻ります。戻り値がさらに引数に渡されるようにすることで入力値に更新されます。

値が更新されない場合

これらは自動でレイアウトされますが、普通は縦に並ぶので横に並べるためにEditorGUILayout.BeginHorizontalメソッドと、EditorGUILayout.EndHorizontalメソッドで囲います。

// 行を開始
EditorGUILayout.BeginHorizontal();

// ラベルを表示
EditorGUILayout.LabelField("x");

// 入力された値を取得
shiftValue.x = EditorGUILayout.FloatField(shiftValue.x * 100f) / 100f;

// ラベルを表示
EditorGUILayout.LabelField("cm");

// 行を終了
EditorGUILayout.EndHorizontal();

これで、軸と入力欄、単位が一行に表示されます。これを3つの軸に対して作りました。

さらに、単位によって場合分けして表示するセットを変えました。

// センチメートルのとき
if (centimeter)
{
    // 行を開始
    EditorGUILayout.BeginHorizontal();

    // ラベルを表示
    EditorGUILayout.LabelField("x");

    // 入力された値を取得
    shiftValue.x = EditorGUILayout.FloatField(shiftValue.x * 100f) / 100f;

    // ラベルを表示
    EditorGUILayout.LabelField("cm");

    // 行を終了
    EditorGUILayout.EndHorizontal();

    // 次の行を開始
    EditorGUILayout.BeginHorizontal();
    EditorGUILayout.LabelField("y");
    shiftValue.y = EditorGUILayout.FloatField(shiftValue.y * 100f) / 100f;
    EditorGUILayout.LabelField("cm");
    EditorGUILayout.EndHorizontal();

    // 次の行を開始
    EditorGUILayout.BeginHorizontal();
    EditorGUILayout.LabelField("z");
    shiftValue.z = EditorGUILayout.FloatField(shiftValue.z * 100f) / 100f;
    EditorGUILayout.LabelField("cm");
    EditorGUILayout.EndHorizontal();

}
// メートルのとき
else
{
    EditorGUILayout.BeginHorizontal();
    EditorGUILayout.LabelField("x");
    shiftValue.x = EditorGUILayout.FloatField(shiftValue.x);
    EditorGUILayout.LabelField("m");
    EditorGUILayout.EndHorizontal();

    EditorGUILayout.BeginHorizontal();
    EditorGUILayout.LabelField("y");
    shiftValue.y = EditorGUILayout.FloatField(shiftValue.y);
    EditorGUILayout.LabelField("m");
    EditorGUILayout.EndHorizontal();

    EditorGUILayout.BeginHorizontal();
    EditorGUILayout.LabelField("z");
    shiftValue.z = EditorGUILayout.FloatField(shiftValue.z);
    EditorGUILayout.LabelField("m");
    EditorGUILayout.EndHorizontal();
}

最後に、変更した値をSerializedPropertyのVector3のプロパティ値に入れ、プロパティの変更を適用します。

// プロパティの値を入力値に変更
shift.vector3Value = shiftValue;

// プロパティの変更を適用
serializedObject.ApplyModifiedProperties();

ボタンはGUILayout.Buttonメソッドで表示できます。クリックされたときにtrueが返るので、if文を使ってクリックされたときの処理を書いています。

if(GUILayout.Button("単位を変える"))
{
    // キーボードフォーカスを切る
    EditorGUI.FocusTextInControl(null);

    // 切り替える
    centimeter = !centimeter;
}

ここではbool変数を切り替えるだけでなく、EditorGUI.FocusTextInControlメソッドの引数にnullを渡して、入力欄へのフォーカスを切っています。フォーカスされたまま単位を変えると、その入力欄だけ値が変わりません。

コメントを残す

メールアドレスが公開されることはありません。