Edited at
UnityDay 5

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

More than 5 years have passed since last update.


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で監視して更新をかけるという手法でもいいかもしれません。