今回はUnityのEditor拡張を利用してインスペクタで表示されるデータのカスタマイズをしてみたいと思います。
エディターの拡張を使えるようになるとゲーム開発の効率がよくなります。
よくなりますと適当に言っている感じは否めないですが・・・・(^_^;)
ゲーム開発を一人でやる人もいますが通常は複数人が関わってゲームを開発する事が多いと思います。
例えば『ゲーム全般を見る人』『キャラクター等の制作を担当する人』『プログラミングをする人』『ゲームの音楽を担当する人』等です。
キャラクターを制作した人がゲーム内でのキャラクターの動きを確認したい時にキャラクターを動かす時のパラメータを操作し動きを細かく確認したいとします。
ですが通常はインスペクタで設定のパラメータと値だけが表示されており、プログラミングをした人はどんなパラメータでどのように調整すればこうなる。
と言う事は知っていてもデザイナー側からはどう操作していいかわからない事があります。
そんな時にどんな人でもわかりやすいように値の設定やサンプル値の設定等が出来たら便利ですよね?
エディターを拡張する事でインスペクタの表示を変えたり、わかりやすく設定値を変更出来るようにしておくわけです。
うーん・・・・わたくしの場合は一人で開発するからエディター拡張の利点をそれほど感じる事はないですし説得力ないですね・・・・・(-_-)
とりあえず便利なんですよー (;一_一)
エディター拡張の仕方
エディタ拡張は作成したスクリプト、例えばキャラクター用の操作スクリプトを使って拡張しインスペクタの表示を変更します。
その為、まずは拡張したいスクリプトを1つ用意する必要があります。
拡張される側のスクリプトの用意
今回拡張するスクリプトはMyDataという名前のスクリプトにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | using UnityEngine; using System.Collections; using System; public class MyData : MonoBehaviour { public int hp; // HP public int attackPower; // 攻撃力 public float speed; // 歩くスピード public float dashSpeed; // 走るスピード public GameObject weapon; // 装備している武器 public GUIStyle style; // インスペクタで使用するスタイル public string testString = "テスト用文字列"; // テスト用文字列 // テスト用文字列をコンソールに表示 public void GetTestString() { Debug.Log (testString); } } |
通常通りMonoBehaviourクラスを継承してMyDataクラスを作成しています。
キャラクターのHPや攻撃力等のフィールド、またエディター拡張のテスト用にスタイルや文字列を用意しています。
実用的なスクリプトではありませんが、キャラクターの持っているパラメータはこんな感じのデータを持っている事が多いと思います。
MyDataスクリプトは
↑のような階層に作成しました。
スクリプトを拡張する側のスクリプトの作成
サブタイトルが解り辛くなってますが・・・、さきほど作成したMyDataを拡張するスクリプトの作成の事ですね。
まず最初に注意しなければいけないのは拡張する側のスクリプトはEditorフォルダ内に置かなければいけないという事です。
Editorという名前を付けたフォルダは特殊フォルダとなります。

拡張される側のスクリプト(MyData)はEditorフォルダ内に置いてはいけません。
EditorフォルダはAssetsの直下に作成した方がいいかもしれません。
それ以外の場所、例えばAssets/MyScript/Editorの中に拡張する側のスクリプトを置いて試したんですが動作がうまくいかなかったので・・・・。
なのでまずはAssets/Editorフォルダを作成します。
今回は拡張する側のスクリプト名をMyDataEditorという名前で作り、Assets/Editor/EditorTestフォルダの中に入れます。
拡張する側のスクリプトはEditorフォルダの中に作ったフォルダ内においても大丈夫です。
↑のような感じになります。
これでエディター拡張をする準備が整いました。
エディターを拡張する為の記述
それではMyDataEditorスクリプトに処理を記述していきましょう。
まずはエディターを拡張する時に必要になる記述です。
1 2 3 4 5 6 7 | // カスタマイズするクラスを設定 [CustomEditor(typeof(MyData))] // Editorクラスを継承してクラスを作成 public class MyDataEditor : Editor { } |
クラスはEditorクラスを継承して作成します。
また、クラスにはCustomEditorアトリビュートを取りつけ、引数に拡張される側のスクリプト名をtypeofで指定します。
こうする事でMyDataスクリプトを拡張するMyDataEditorクラスを作成出来ます。
MyDataのデータを取得する
MyDataEditorはMyDataスクリプトの拡張をするクラスなのでMyDataで宣言されているhpやattackPowerなどのデータにアクセスする手段が必要です。
SerializedProperty
その為に使用するのがSerializedPropertyです。
Editorクラスで定義されているserializedObjectのFindPropertyを利用して取得する事が出来ます。
1 2 3 4 5 6 7 | // OnEnableメソッド内等で記述し、MyDataのhpプロパティの取得 hp = serializedObject.FindProperty ("hp"); // OnInspectorGUIメソッド内で記述し、プロパティ値をインスペクタに表示 EditorGUILayout.PropertyField (hp); |
↑のようにプロパティを取得し、その値を表示する事が出来ます。
OnInspectorGUIメソッドについては後述します。
target
もうひとつの方法がEditorクラスで宣言されているtargetを使用する事です。
targetには拡張される側のクラス(MyData)オブジェクトが入っているのでそれをMyDataにキャストし使用します。
1 2 3 4 5 6 7 | // targetでデータを取得しキャスト MyData myData = (MyData) target; // speedプロパティを表示する myData.speed = EditorGUILayout.FloatField ("speed", myData.speed); |
OnInspectorGUIメソッド内に記述します。
targetで得たMyDataクラスからspeedの値を取得し、インスペクタに表示しそれを再度myData.speedに入れています。
OnInspectorGUI
インスペクタのデータを更新すると呼ばれるのがOnInspectorGUIメソッドです。
その為エディターを拡張する側のスクリプトでインスペクタの値を表示するにはOnInspectorGUIメソッドを記述しその中に処理を書きます。
このOnInspectorGUIメソッドはoverrideをします。
シリアライズオブジェクトのデータを使う場合はserializedObject.UpdateとserializedObject.ApplyModifiedPropertiesを使います。
1 2 3 4 5 6 7 8 9 10 11 12 13 | public override void OnInspectorGUI() { // シリアライズオブジェクトの更新 serializedObject.Update (); // ここにシリアライズデータの表示をする // シリアライズオブジェクトのプロパティの変更を更新 serializedObject.ApplyModifiedProperties (); } |
↑のようにシリアライズデータを挟む形で記述します。
インスペクタにボタンを表示
インスペクタにボタンを表示するにはGUILayout.Buttonを使用します。
1 2 3 4 | if (GUILayout.Button ("ボタンに表示するテキスト")) { } |
↑のように記述するとインスペクタにボタンが表示されボタンを押した時の処理はif文内に書きます。
EditorGUILayout.IntSlider
EditorGUILayout.IntSliderを使用するとint型のスライダーを作成する事が出来ます。
1 2 3 4 | // hpシリアライズデータを表示 EditorGUILayout.IntSlider (hp, 0, 100); |
↑のようにhpシリアライズデータを0~100の間で変更するスライダーを作成する事が出来ます。
EditorGUI.ProgressBar
EditorGUI.ProgressBarでプログレスバーを表示する事が出来ます。
1 2 3 4 | Rect rect = GUILayoutUtility.GetRect (10, 20); EditorGUI.ProgressBar (rect, hp.intValue / 100f, "hp"); |
指定した領域(Rect)でhpの値のパーセンテージをプログレスバーで表示しています。
EditorGUILayout.MinMaxSlider
EditorGUILayout.MinMaxSliderを使用すると最小値と最大値をバーでつないだスライダーを使用する事が出来ます。
1 2 3 | EditorGUILayout.MinMaxSlider (ref minVal, ref maxVal, 0, 30); |
最初の引数が現在の最小値、2番目の引数が現在の最大値、3番目の引数が設定出来る最小値、4番目の引数が設定出来る最大値
EditorGUILayout.BeginHorizontal
インスペクタで並びを整列する時に使用します。
EditorGUILayout.BeginHorizontalが横の始まりでEditorGUILayout.EndHorizontalが終わりです。
縦の整列をしたい時はHorizontalの部分をVerticalに変更します。
エディターの変更値を保存
Unityのゲームでデータを保存するにはPlayerPrefsを使用すると出来ますが、

エディターの設定等を保存したい時はEditorPrefsを使用する事で出来ます。
使い方はPlayerPrefsと同じように使う事が出来ます。
1 2 3 | EditorPrefs.SetBool ("TEST", true); |
とするとTESTというEditorPrefsデータが作成され、値がtrueに設定されます。
EditorPrefsデータの保存先はMacOSとWindowsでは異なります。
https://docs.unity3d.com/jp/530/ScriptReference/EditorPrefs.html
EditorGUILayout.HelpBox
EditorGUILayout.HelpBoxを使うとインスペクタにヘルプボックスを表示する事が出来ます。
1 2 3 | EditorGUILayout.HelpBox ("表示するテキスト", メッセージの種類を指定); |
↑のように使用します。
メッセージの種類を指定する事でヘルプの内容なのか注意喚起なのかといったメッセージの種類を設定出来ます。
CanEditMultipleObjectsアトリビュート
エディター拡張をする側のスクリプトにCanEditMultipleObjectsアトリビュートを付けるとされる側のスクリプト(MyData)を複数のゲームオブジェクトに設定し、
それらのゲームオブジェクトを複数選択した時に共通のインスペクタの項目を一緒に変更する事が出来ます。
このアトリビュートが設定されていない場合は同じスクリプトが設定されていても同時に値を変更する事は出来ません。
↑がCanEditMultipleObjectsアトリビュートをクラスに付けていない時の表示です。
同じMyDataスクリプトを取りつけた複数のクラスを選択しても値の変更が出来ません。
↑がCanEditMultipleObjectsを付けた場合です。
MyDataスクリプトを取りつけた複数のゲームオブジェクトを選択しインスペクタで項目の値を変更すると選択したゲームオブジェクト全てのMyDataの項目値が変更されます。
DrawDefaultInspector
インスペクタに元々ある値はそのまま使いたい場合はSerializedPropertyやtargetから値を取得しなくても、
DrawDefaultInspectorメソッドを呼び出せば拡張される側のMyDataのフィールドがそのままインスペクタに表示されます。
インスペクタをカスタマイズするサンプル
簡単なカスタマイズの説明だけになりましたが、MyDataEditorスクリプトにサンプルを記述し確認してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | using UnityEngine; using System.Collections; using UnityEditor; // カスタマイズするクラスを設定 [CustomEditor(typeof(MyData))] // 複数オブジェクトを選択した時に変更可能にする [CanEditMultipleObjects] // Editorクラスを継承してクラスを作成 public class MyDataEditor : Editor { // シリアライズプロパティ SerializedProperty hp; SerializedProperty attackPower; SerializedProperty weapon; // MinMaxSlider用の初期の値 float minVal; float maxVal; void OnEnable() { // シリアライズプロパティの取得 hp = serializedObject.FindProperty ("hp"); attackPower = serializedObject.FindProperty ("attackPower"); weapon = serializedObject.FindProperty ("weapon"); } // インスペクタが更新されたら呼ばれる public override void OnInspectorGUI() { // シリアライズオブジェクトの更新 serializedObject.Update (); // targetでデータを取得しキャスト MyData myData = (MyData) target; // 値をスライダーにする EditorGUILayout.IntSlider (hp, 0, 100); EditorGUILayout.IntSlider (attackPower, 0, 100); // プログレスバーでhp、attackPowerを表示 Rect rect = GUILayoutUtility.GetRect (10, 20); EditorGUI.ProgressBar (rect, hp.intValue / 100f, "hp"); EditorGUILayout.Space (); rect = GUILayoutUtility.GetRect (10, 20); EditorGUI.ProgressBar (rect, attackPower.intValue / 100f, "attackPower"); // MinMaxSliderを使用してspeed、dashSpeedの値を変える minVal = EditorGUILayout.FloatField ("speed", myData.speed); maxVal = EditorGUILayout.FloatField ("dashSpeed", myData.dashSpeed); EditorGUILayout.MinMaxSlider (ref minVal, ref maxVal, 0, 30); // 変更された値をデータに反映 myData.speed = minVal; myData.dashSpeed = maxVal; myData.speed = EditorGUILayout.FloatField ("speed", myData.speed); myData.dashSpeed = EditorGUILayout.FloatField ("dashSpeed", myData.dashSpeed); EditorGUILayout.PropertyField (weapon); // 並びを調整して表示 EditorGUILayout.BeginHorizontal (); GUILayout.Label ("TEST"); EditorGUILayout.LabelField ("Test"); EditorGUILayout.EndHorizontal (); // スタイルを指定してテキストとテクスチャを表示 EditorGUILayout.LabelField ("TesT", myData.style, GUILayout.MaxWidth (500)); // シリアライズオブジェクトのプロパティの変更を更新 serializedObject.ApplyModifiedProperties (); // targetからインスペクタデータを取得 myData.hp = EditorGUILayout.IntField ("hp", myData.hp); myData.attackPower = EditorGUILayout.IntField ("attackPower", myData.attackPower); // デフォルトのインスペクタを表示 DrawDefaultInspector (); // インスペクタにボタンを表示し押したら値をリセット if (GUILayout.Button ("全てのパラメータをリセット")) { myData.hp = 0; myData.attackPower = 0; myData.speed = 0f; myData.dashSpeed = 0f; myData.weapon = null; Debug.Log ("パラメータをリセットしました"); } // エディターの設定を取得 if (EditorPrefs.HasKey ("TEST")) { if (EditorPrefs.GetBool ("TEST")) { Debug.Log ("エディター設定がセットされている"); } else { Debug.Log ("エディター設定がセットされていない"); } } // エディターの設定を保存 if (GUILayout.Button ("値をセット・リセット")) { EditorPrefs.SetBool ("TEST", !EditorPrefs.GetBool ("TEST")); } // エディターの設定を削除 if (GUILayout.Button ("エディター設定を削除")) { if(EditorPrefs.HasKey ("TEST")) { EditorPrefs.DeleteKey ("TEST"); Debug.Log("エディター設定を削除しました。"); } } if (GUILayout.Button ("MyDataのGetTestStringメソッドを実行")) { myData.GetTestString (); } EditorGUILayout.HelpBox ("ここでインスペクタ項目終わり", MessageType.Info); } } |
SerializedPropertyを使ったやり方とtargetを使ったやり方でインスペクタに値を表示しています。
MinMaxSliderの最小値と最大値では最小値をspeed、最大値をdashSpeedに設定するようにしています。
1 2 3 | EditorGUILayout.LabelField ("TesT", myData.style, GUILayout.MaxWidth (500)); |
でインスペクタで設定したStyleによってラベルを表示しています。
ラベルはテクスチャやテキストが設定出来ます。
テクスチャとテキストを両方使いたい場合はGUIContentを作成すると一緒に使えます。
1 2 3 4 5 | public Texture texture; EditorGUILayout.LabelField (new GUIContent("TesT", texture), myData.style, GUILayout.MaxWidth (500)); |
とします。
他にはインスペクタにボタンを作成し、インスペクタの値をリセットしたり、EditorPrefsにTESTというデータを操作する処理です。
サンプルが出来たので確認してみましょう。
↑のようになりました。
通常のインスペクタとはずいぶん変わりましたね。
EditorGUILayout.LabelField (“TesT”, myData.style, GUILayout.MaxWidth (500));
の行でエラーが発生する時があります(横幅が足りない時?)。
今回紹介したエディターのカスタマイズ方法の他にも出来る事はあるので確認してみてください。