手抜きしてインスペクター拡張

  • 51
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

Unityで手抜きしてインスペクターを拡張する方法

手抜きその1 「エディタ拡張せずに、インスペクターから更新」

サンプルとしてUnity4.3で使用可能になったSpriteの表示サイズを変更するコンポーネントを作ってみましょう。
「インスペクター上で入力されたサイズの値によって、Transformのスケール値を変更する」というクラスです。

ScreenClip [2].png

CustomSprite.cs

using UnityEngine;
[ExecuteInEditMode]
[AddComponentMenu("2D/CustomSprite")]
[RequireComponent (typeof (SpriteRenderer))]
public class CustomSprite : MonoBehaviour
{
    //表示スプライトのサイズ
    [SerializeField]
    Vector2 customSize = Vector2.zero;
    public Vector2 Size { 
        get { return customSize; }
        set { customSize = value; RefreshSize(); }
    }

    //スプライトの基本のサイズ
    Vector2 baseSize = Vector2.zero;
    public Vector2 BaseSize { get { return baseSize; } }

    void Awake()
    {
        RefreshSize();
    }

    void Update()
    {
        //毎フレーム更新?重い処理には使えない
        //RefreshSize()
    }

    //インスペクターの値が変更されるたびに呼ばれる
    void OnValidate()
    {
        RefreshSize();
    }

    //インスペクターの値に合わせて表示する大きさを変える
    void RefreshSize()
    {
        Sprite sprite = this.GetComponent<SpriteRenderer>().sprite;
        if (null != sprite)
        {
            baseSize = new Vector2(sprite.rect.width, sprite.rect.height);
            this.transform.localScale = new Vector3(customSize.x / BaseSize.x, customSize.y / BaseSize.y, 1);
        }
    }
}

ポイントは、OnValidate()です。
これは、インスペクターの値が変化した際に呼ばれるコールバックです。
「インスペクターの見た目は変えなくていいけど、値の変化はリアルタイムに反映させたい」
という場合は、エディター拡張せずにこれを使えばOKです。
Unity4.2以降での追加機能になるため、機能の強力さのわりには意外に知られてないかと思います。

手抜きその2 「カスタムインスペクターをなるべく簡単に書く」

さて、スプライトのサイズを変更できるようにはなりましたが、
使ってみると「元のサイズに戻す」ボタンが欲しくなってきました。
となると、エディター拡張でカスタムインスペクターのクラスを書くしかないですが、
「元の表示はほぼそのままでボタンを追加するだけ」なので、できるなら簡単に書きたいものです。
ついでに、表示するラベル名もCustomSize ではなくSizeにしてみましょう。

ScreenClip [3].png

CustomSpriteInspector.cs

using UnityEngine;
using UnityEditor;

[CanEditMultipleObjects]
[CustomEditor(typeof(CustomSprite))]
public class CustomSpriteInspector : Editor
{
    public override void OnInspectorGUI()
    {
        serializedObject.Update();
        DrawProperties();
        serializedObject.ApplyModifiedProperties();
    }

    //各要素の描画
    void DrawProperties()
    {
        CustomSprite obj = target as CustomSprite;
        EditorGUILayout.BeginHorizontal();

        //PropertyFieldを使えば、型に合わせて基本通り描画してくれる。
        //面倒なUndo処理とかも書かなくてもいい、
        EditorGUILayout.PropertyField(serializedObject.FindProperty("customSize"), new GUIContent("Size"));

        //リセットボタン
        if (GUILayout.Button("Reset", GUILayout.Width(50f)))
        {
            //こういう独自処理はUndo書く必要がある
            Undo.RecordObject(obj, "CustomSize Reset");
            obj.Size = obj.BaseSize;
            EditorUtility.SetDirty(target);
        }

        EditorGUILayout.EndHorizontal();
    }
}

ポイントは、EditorGUILayout.PropertyField()です。
これを使えば、IntFieldだの、Vector2Fieldだの、ColorFieldだのと使い分ける必要がなくなります。
元の型に合わせて表示されるので、とっても楽。
おまけにUndo処理もやってくれます。
サンプルでは一つだけしかパラメーターがないですが、何個もパラメーターがあるのが普通なので、そういった場合は特に効果を実感できると思います。
ただし、serializedObject.FindPropertyではメンバ変数を文字列で検索するため、元のメンバ変数をリネームするときだけは注意してください。
(Unityではリネームはそうそうしないと思いますが)

実際にエディタ拡張していると
基本はデフォルト表示のままでOKだけど、
「一部だけ変えたい」
「レイアウトを変えたり表示ラベル名を変えたい」
「ちょっとしたショートカット機能のボタンだけつけたい」

ということが殆どだと思います。
そんな場合は、このEditorGUILayout.PropertyField()を使えば、とても楽になります。
上記のOnValidateと合わせて使えば、もろもろの更新も自動でかけることができるため、かなりスッキリとコードが書けます。

注)

ただし、唯一の弱点が。
OnValidateで読んだ際には、子オブジェクトなどGameObjectの作成を自動で作成することができないようです。
参考 http://anchan828.hatenablog.jp/entry/2013/11/18/012021
こういった場合は、OnValidate内ではisChanedなどのフラグだけ立てておいて、Updateで監視して更新をかけるという手法でもいいかもしれません。