#Unityで手抜きしてインスペクターを拡張する方法
##手抜きその1 「エディタ拡張せずに、インスペクターから更新」
サンプルとしてUnity4.3で使用可能になったSpriteの表示サイズを変更するコンポーネントを作ってみましょう。
「インスペクター上で入力されたサイズの値によって、Transformのスケール値を変更する」というクラスです。
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にしてみましょう。
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で監視して更新をかけるという手法でもいいかもしれません。