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

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


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

![ScreenClip [2].png](https://qiita-image-store.s3.amazonaws.com/0/12158/a6715586-b19e-7e26-d7d1-72e3f62b8e22.png)



```csharp: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](https://qiita-image-store.s3.amazonaws.com/0/12158/7d73072e-344a-b2ce-2b4b-e50de917ce5c.png)


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