丸い爪でも立ててみる

Unity開発の事一覧

回 ScriptableObjectのインスペクターをカスタマイズしてみた【Unity】

はじめまして、すらです。

今回は自分に向けた備忘録記事です。

ScriptableObjectのインスペクターを自分好みにカスタマイズしようと思います。

 

初期のScriptableObjectだと、変数の数が多くなったときにかなり見にくいです。

ScriptableObjectのスクリプトで

[Header("wordの説明文")]
public string word;
のようにすればインスペクターで変数の上に説明を出すことができますが、
勉強がてら、インスペクターをフルカスタマイズしてみようと思いました。

 

①Assets/Editorフォルダーにスクリプトを設置

AssetsフォルダにEditorがない場合は作成してください。

スクリプト名に決まりはないので、任意のもので大丈夫です。

私はItemDataというScriptableObjectを作成したので、ItemDataEditor.csとして作成しました。

 

②スクリプトの記述


using UnityEditor;

[CustomEditor(typeof(ItemData))] // ItemDataはScriptableObjectのクラス名
public class ItemDataEditor : Editor // Editorを継承
{
    SerializedProperty itemType;
    SerializedProperty itemId;
    SerializedProperty itemName;
    SerializedProperty itemPrefab;
    SerializedProperty itemRotate;
    SerializedProperty itemPosition;
    SerializedProperty handAnimationType;
    SerializedProperty interactRotate;
    SerializedProperty interactPosition;
    SerializedProperty interactScale;
    SerializedProperty showRotate;
    SerializedProperty xBaseRotate;
    SerializedProperty xRotateRange;
    SerializedProperty yBaseRotate;
    SerializedProperty yRotateRange;

    void OnEnable()
    {
        // ScriptableObjectの変数を取得しておく
        itemType = serializedObject.FindProperty("itemType");
        itemId = serializedObject.FindProperty("itemId");
        itemName = serializedObject.FindProperty("itemName");
        itemPrefab = serializedObject.FindProperty("itemPrefab");
        itemRotate = serializedObject.FindProperty("itemRotate");
        itemPosition = serializedObject.FindProperty("itemPosition");
        handAnimationType = serializedObject.FindProperty("handAnimationType");
        interactRotate = serializedObject.FindProperty("interactRotate");
        interactPosition = serializedObject.FindProperty("interactPosition");
        interactScale = serializedObject.FindProperty("interactScale");
        showRotate = serializedObject.FindProperty("showRotate");
        xBaseRotate = serializedObject.FindProperty("xBaseRotate");
        xRotateRange = serializedObject.FindProperty("xRotateRange");
        yBaseRotate = serializedObject.FindProperty("yBaseRotate");
        yRotateRange = serializedObject.FindProperty("yRotateRange");
        /*
            ItemData itemData = (ItemData)target; このようにScriptableObject自体を取得することもできる
            itemData.itemType などでアクセスできる
        */
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update();  // これを入れないと変更が反映されない場合がある
       
     // ここにカスタマイズしたインスペクターを記述

        serializedObject.ApplyModifiedProperties(); // これを入れないと変更が反映されない場合がある
        
    }
}

 

FindPropertyの中身はScriptableObjectの変数名です。

OnInspectorGUIの中身を書いていきます。

インスペクターをカスタマイズするときに使う最も基本的なものをいくつかご紹介。

 

PropertyField

EditorGUILayout.PropertyField(XXX);

ScriptableObjectの変数を表示させます。(xxxは変数名)

 

LabelField

EditorGUILayout.LabelField("ラベルの文字", エディタースタイル);

変数の上にラベルを設置できます。主に変数の名前などに使用すると思います。

エディタースタイルはUnityで用意されているテンプレートのようなものを適用させるイメージです。

いくつか紹介。

EditorStyles.label 通常のラベル
EditorStyles.centeredGreyMiniLabel 太字 のラベル
EditorStyles.wordWrappedLabel 小さいグレーの中央揃えラベル
EditorStyles.miniLabel 折り返しありのラベル
EditorStyles.miniBoldLabel 小さいラベル
EditorStyles.whiteLabel 小さい太字ラベル

 

HelpBox

EditorGUILayout.HelpBox("説明文", メッセージタイプ);

こちらはラベルとは違い、アイコンと枠線で囲まれた説明文を表示します。

EditorGUILayout.HelpBox("説明文が入ります。\n改行しました。", メッセージタイプ);

「\n」で改行できます。

メッセージタイプでアイコンを選択できます。

EditorGUILayout.HelpBox("MessageType.Info", MessageType.Info);
EditorGUILayout.HelpBox("MessageType.Error", MessageType.Error);
EditorGUILayout.HelpBox("MessageType.Warning", MessageType.Warning);
EditorGUILayout.HelpBox("MessageType.None", MessageType.None);

 

indentLevel

EditorGUI.indentLevel++;
EditorGUI.indentLevel--;

インスペクターにインデントをつけられる。

インデントを足したい場合はEditorGUI.indentLevel++;

インデントを消したい場合はEditorGUI.indentLevel–;

さっきのHelpBoxに適用してみる。

EditorGUILayout.HelpBox("MessageType.Info", MessageType.Info);
EditorGUI.indentLevel++;
EditorGUILayout.HelpBox("MessageType.Error", MessageType.Error);
EditorGUI.indentLevel++;
EditorGUILayout.HelpBox("MessageType.Warning", MessageType.Warning);
EditorGUI.indentLevel -= 2;
EditorGUILayout.HelpBox("MessageType.None", MessageType.None);

あたりまえですが

EditorGUI.indentLevel++;

EditorGUI.indentLevel += 1;

これらは同義なので、

EditorGUI.indentLevel += 10;
EditorGUI.indentLevel -= 5;

といったように一度にたくさんインデントをつける/消すことも可能。

 

Space

EditorGUILayout.Space();
EditorGUILayout.Space(30); // px指定

間隔を開けたいときに使います。

()はpxで間隔を指定できます。

 


 

ほかにもインスペクターをカスタマイズする方法はたくさんありますが、いったんはこれらを知っているだけで見やすいインスペクターを作成できると思います。

 

 

③実際に私のScriptableObjectのインスペクターをカスタマイズしてみる

Enumの値によって表示項目を切り替える

↑これがカスタマイズする前の初期表示。

まず、私の環境ではitemTypeというものをenumで選べるようにしています。enumの値は3種類あり、

調べる際に画面中央でさらにアイテムをぐりぐり回して見ることができるInteract。

調べると手に持つことができるInventory。

その他(未実装)がOther。

 

例えば、Interactを選んだ場合、HandAnimationTypeの値は使わない。

Inventoryを選んだ場合はitemId、itemName、itemPrefab、HandAnimationType以外は使わないといったように、選んだenumによって使わない項目があります。

そういったものは非表示にしたほうがスッキリして見やすくなりますので、そのようにカスタマイズしてみます。

 

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

        var itemTypeNum = itemType.enumValueIndex; // 選択したEnumのインデックスを取得

        EditorGUILayout.PropertyField(itemType);
        EditorGUILayout.PropertyField(itemId);
        EditorGUILayout.PropertyField(itemName);
        EditorGUILayout.PropertyField(itemPrefab);
        if(itemTypeNum == 0) // Inventory
        {
            EditorGUILayout.PropertyField(handAnimationType);
        }
        else if(itemTypeNum == 1) // Interact
        {
            EditorGUILayout.PropertyField(itemRotate);
            EditorGUILayout.PropertyField(itemPosition);
            EditorGUILayout.PropertyField(interactRotate);
            EditorGUILayout.PropertyField(interactPosition);
            EditorGUILayout.PropertyField(interactScale);
            EditorGUILayout.PropertyField(showRotate);
            EditorGUILayout.PropertyField(xBaseRotate);
            EditorGUILayout.PropertyField(xRotateRange);
            EditorGUILayout.PropertyField(yBaseRotate);
            EditorGUILayout.PropertyField(yRotateRange);
        }
        else if(itemTypeNum == 2) // Other
        {

        }

        serializedObject.ApplyModifiedProperties();
}

 

enumの値によって表示する項目を変更することができました。

 

◇解説

var itemTypeNum = itemType.enumValueIndex;

選択したEnumのインデックスを取得します。

例えば、ScriptableObjectで以下のように設定されているとします。

// アイテムタイプ
public enum ItemType
{
    Inventory, // インベントリに入るアイテム
    Interact, // インタラクトモードで調べるアイテム
    Other, // その他の方法で保持するアイテム
}


// アイテムタイプ
public ItemType itemType;

この場合

Inventoryが0

Interactが1

Otherが2

というようにインデックスがふられています。

これを取得することによって、今選んでいるEnumの値を条件式に使えるというわけです。

 

完成形

 

あとは最初に紹介したラベルやHelpBox、インデントでさらに見やすく調整してみました。

using UnityEditor;


[CustomEditor(typeof(ItemData))]
public class ItemDataEditor : Editor
{
    SerializedProperty itemType;
    SerializedProperty itemId;
    SerializedProperty itemName;
    SerializedProperty itemPrefab;
    SerializedProperty itemRotate;
    SerializedProperty itemPosition;
    SerializedProperty handAnimationType;
    SerializedProperty interactRotate;
    SerializedProperty interactPosition;
    SerializedProperty interactScale;
    SerializedProperty showRotate;
    SerializedProperty xBaseRotate;
    SerializedProperty xRotateRange;
    SerializedProperty yBaseRotate;
    SerializedProperty yRotateRange;


    void OnEnable()
    {
        itemType = serializedObject.FindProperty("itemType");
        itemId = serializedObject.FindProperty("itemId");
        itemName = serializedObject.FindProperty("itemName");
        itemPrefab = serializedObject.FindProperty("itemPrefab");
        itemRotate = serializedObject.FindProperty("itemRotate");
        itemPosition = serializedObject.FindProperty("itemPosition");
        handAnimationType = serializedObject.FindProperty("handAnimationType");
        interactRotate = serializedObject.FindProperty("interactRotate");
        interactPosition = serializedObject.FindProperty("interactPosition");
        interactScale = serializedObject.FindProperty("interactScale");
        showRotate = serializedObject.FindProperty("showRotate");
        xBaseRotate = serializedObject.FindProperty("xBaseRotate");
        xRotateRange = serializedObject.FindProperty("xRotateRange");
        yBaseRotate = serializedObject.FindProperty("yBaseRotate");
        yRotateRange = serializedObject.FindProperty("yRotateRange");


        /*
            ItemData itemData = (ItemData)target; このようにScriptableObject自体を取得することもできる
            itemData.itemType などでアクセスできる
        */
    }


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


        var itemTypeNum = itemType.enumValueIndex; // 選択したEnumのインデックスを取得
        EditorGUILayout.HelpBox("アイテムタイプ\nInventory : 手に持つアイテム\nInteract : インタラクトモードに入るアイテム\nOther : その他(未実装)", MessageType.Info);
        EditorGUILayout.PropertyField(itemType);
        EditorGUI.indentLevel++;
        EditorGUILayout.Space(10);
        EditorGUILayout.PropertyField(itemId);
        EditorGUILayout.PropertyField(itemName);
        EditorGUILayout.PropertyField(itemPrefab);
        if(itemTypeNum == 0) // Inventory
        {
            EditorGUILayout.Space(10);
            EditorGUI.indentLevel--;
            EditorGUILayout.HelpBox("アニメーションタイプ\n1 : 手でつかむアニメーション\n2 : 手のひらに乗せるアニメーション", MessageType.Info);
            EditorGUI.indentLevel++;
            EditorGUILayout.PropertyField(handAnimationType);
            EditorGUILayout.Space(10);
            EditorGUI.indentLevel--;
            EditorGUILayout.LabelField("アイテムを手に持ったときのアイテムの位置と回転");
            EditorGUI.indentLevel++;
            EditorGUILayout.PropertyField(itemRotate);
            EditorGUILayout.PropertyField(itemPosition);


        }
        else if(itemTypeNum == 1) // Interact
        {
            EditorGUILayout.Space(10);
            EditorGUI.indentLevel--;
            EditorGUILayout.LabelField("アイテムをインタラクトする前の位置と回転とスケール、自動的に代入される");
            EditorGUI.indentLevel++;
            EditorGUILayout.PropertyField(interactRotate);
            EditorGUILayout.PropertyField(interactPosition);
            EditorGUILayout.PropertyField(interactScale);
            EditorGUILayout.Space(10);
            EditorGUI.indentLevel--;
            EditorGUILayout.LabelField("インタラクトモードに入ったときのアイテムの初期回転");
            EditorGUI.indentLevel++;
            EditorGUILayout.PropertyField(showRotate);
            EditorGUILayout.Space(10);
            EditorGUI.indentLevel--;
            EditorGUILayout.LabelField("基準となる回転数値と回転の範囲(X)");
            EditorGUI.indentLevel++;
            EditorGUILayout.PropertyField(xBaseRotate);
            EditorGUILayout.PropertyField(xRotateRange);
            EditorGUILayout.Space(10);
            EditorGUI.indentLevel--;
            EditorGUILayout.LabelField("基準となる回転数値と回転の範囲(Y)");
            EditorGUI.indentLevel++;
            EditorGUILayout.PropertyField(yBaseRotate);
            EditorGUILayout.PropertyField(yRotateRange);
            EditorGUILayout.Space(10);


        }
        else if(itemTypeNum == 2) // Other
        {


        }


        serializedObject.ApplyModifiedProperties();
    }
}

 

以上です。

サイト改善のため、この記事が参考になった場合グッドをお願いします。
0
ランキング