66
59

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

UnityAdvent Calendar 2013

Day 5

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

Last updated at Posted at 2013-12-04

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

66
59
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
66
59

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?