しゅみぷろ

プログラミングとか

エディター拡張について

めちゃくちゃ参考になるスライド。

www.slideshare.net

インスペクター拡張についてです。内容は初歩的なことです。 とりあえず、インスペクター拡張を試した基本的なサンプルを。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class InspectorExtension : MonoBehaviour
{
  public int testInt;

  [SerializeField]
  private string testString;

  [SerializeField]
  private int[] testIntList = new int[] { 0, 0, 0 };

  public void Start()
  {
    Debug.Log(testInt);
    Debug.Log(testString);
    foreach(var value in testIntList)
      Debug.Log(value);
  }
}
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(InspectorExtension))]
public class InspectorExtensionEditorScript : Editor
{
  private InspectorExtension targetScript;
  private SerializedProperty testString;
  private SerializedProperty intList;
  private int[] intListCache = new int[] { 0, 0, 0 };

  private void OnEnable()
  {
    targetScript = target as InspectorExtension;
    testString = serializedObject.FindProperty("testString");
    intList = serializedObject.FindProperty("testIntList");

    //シリアル化されているデータで初期化する
    for(int i = 0; i < intList.arraySize; ++i)
      intListCache[i] = intList.GetArrayElementAtIndex(i).intValue;
  }

  public override void OnInspectorGUI()
  {
    //publicなフィールドにはtargetプロパティを用いてアクセスすればいい
    GUILayout.BeginHorizontal();
    GUILayout.Label("Int");
    targetScript.testInt = (int)GUILayout.HorizontalSlider(targetScript.testInt, 0, 100);
    GUILayout.Box(targetScript.testInt.ToString());
    GUILayout.EndHorizontal();

    //privateなシリアル化可能メンバにアクセスするにはserializedObjectを使う
    EditorGUILayout.BeginHorizontal();
    GUILayout.Label("String");
    testString.stringValue = GUILayout.TextField(testString.stringValue);
    EditorGUILayout.EndHorizontal();

    //配列(リスト)のサイズを固定し、インスペクターで要素のみ変更可能にしてみる
    GUILayout.Label("IntList");
    GUILayout.BeginHorizontal();
    for(int i = 0; i < 3; ++i)
    {
      intListCache[i] = (int)GUILayout.HorizontalSlider(intListCache[i], 0, 100);
      GUILayout.Label(string.Format("[{0}]:{1}", i, intListCache[i]));
    }
    GUILayout.EndHorizontal();
    for(int i = 0; i < intList.arraySize; ++i)
    {
      var intvalue = intList.GetArrayElementAtIndex(i);
      intvalue.intValue = intListCache[i];
    }

    //serializedObjectの変更を適用する
    serializedObject.ApplyModifiedProperties();
  }
}

このインスペクター拡張では、シリアル化可能なpublic/privateフィールドを操作しています。 また、インスペクターで操作できる配列のサイズを固定にしています。 publicなフィールドを弄る場合はtargetを必要な型にキャストして使えばいいのですが、privateフィールドの場合そのままではアクセスできません。こういう場合はserializedObjectを使ってアクセスします。

この実装の問題は、メンバへのアクセスがちょっと面倒なことです。アクセスが面倒な理由は簡単で、拡張するコンポーネントと別ファイルでインスペクター拡張スクリプトを記述しているからです。プリプロセッサでUnityエディタ上でのみ定義されるクラスとしてインスペクター拡張を定義すればいい感じになります。

とりあえず

  • インスペクター拡張は、拡張したいコンポーネントに直に定義する(プリプロセッサでビルド時に含まれないようにする)とメンバへのアクセスが楽
  • カスタムウィンドウ等はEditorフォルダ以下に配置しておけばビルドターゲットに含まれないのであまり気にしなくていい

って感じでしょうか。