More Related Content
Similar to Extending the Unity Editor
Similar to Extending the Unity Editor (20)
Extending the Unity Editor
- 2. Extending the Unity Editor
まずはUnityのGUIを理解しよう (1/2)
・基本的にデバッグ用
毎フレーム1つ1つのコンポーネントについて判定・再描画が
行われるので重い。また使いこなすにはGUISkinなどの細かい
仕様を覚えなくてはならず面倒。
・Viewport空間ではなくScreen空間に描画
スクリーンに対する絶対座標で描画されるので、画面解像度が
変わった時に破綻する。
・フォントの問題
PC/Mac以外で日本語文字を表示しようとすると非常に面倒。
携帯機ではDynamicフォントが使えない。
2/30
- 3. Extending the Unity Editor
まずはUnityのGUIを理解しよう (2/2)
・OnGUI()に表示と処理を一緒くたに書く
ゲーム実行中に、ゲームオブジェクトの更新と描画が終わった
タイミングでOnGUI()が呼ばれ、そこに記述されている内容が
実行される。
void OnGUI()
{
if (GUILayout.Button("Next Level"))
{
Application.LoadLevel("Level2");
}
if (GUILayout.Button("Quit"))
{
Application.Quit();
}
}
・拡張エディタの場合もこの辺の仕組みは同じ
インスペクタやウィンドウの再描画が必要なタイミングで、
OnInspectorGUI()やOnGUI()が呼ばれる。
3/30
- 4. Extending the Unity Editor
GUIとGUILayout (1/2)
・GUI = 自由配置
第一引数が必ず、位置とサイズを指定するためのRect構造体。
void OnGUI()
{
GUI.Button(new Rect(10, 10, 100, 50), "Button1");
GUI.Button(new Rect(50, 50, 100, 50), "Button2");
GUI.Button(new Rect(90, 90, 100, 50), "Button3");
text = GUI.TextArea(new Rect(200, 10, 100, 100), text);
}
4/30
- 5. Extending the Unity Editor
GUIとGUILayout (2/2)
・GUILayout = 逐次配置
書いていった順に並んで表示される。BeginHorizontalなどで
グループ化して並べることも可能。
void OnGUI()
{
GUILayout.BeginHorizontal(); //水平方向にグループ化
GUILayout.BeginVertical(); //垂直方向にグループ化
GUILayout.Button("Button1");
GUILayout.Button("Button2");
GUILayout.Button("Button3");
GUILayout.EndVertical();
text = GUILayout.TextArea(text, GUILayout.Width(200), GUILayout.Height(60));
GUILayout.EndHorizontal();
}
※インデントは気分。必須ではない。
5/30
- 6. Extending the Unity Editor
EditorGUIとEditorGUILayout (1/2)
・GUI・GUILayoutの関係と同じ
EditorGUIは絶対座標指定、EditorGUILayoutは逐次配置。
Editor向けに便利なパーツが用意されている。
・GUI系とEditorGUI系は混在可能
というよりEditorGUIにはButton等の基本的なパーツはなく、
GUI系と混在させて使うことが前提。
public override void OnInspectorGUI()
{
GUILayout.BeginHorizontal();
position = EditorGUILayout.Vector3Field("Position", position);
if (GUILayout.Button("Reset"))
position = Vector3.zero;
GUILayout.EndHorizontal();
}
6/30
- 7. Extending the Unity Editor
EditorGUIとEditorGUILayout (2/2)
・基本的にEditorGUILayoutを使用
ゲーム用のGUIがゲーム画面の左上を基準にして配置されるよ
うに、エディタ用のGUIはインスペクタやウィンドウの左上を
基準にして配置されていく。
座標を全部自前で計算してもいいが、EditorGUILayoutで配置
しておくとスケーリングが自動で行われるのでスマート。
・位置調整用のパーツもある
領域のサイズを考慮して自動的に伸縮するFlexibleSpace()や
逆に勝手に伸縮しないようにするGUILayout.ExpandWidth()
を上手く使ってかっこ良く配置しよう。
7/30
- 8. Extending the Unity Editor
インスペクタを拡張してみよう (1/5)
・何を拡張できるの?
インスペクタに表示されるものなら何でも。
選択中のオブジェクトに対する操作やインスペクタでの表示の
され方を独自に記述可能。
・コンポーネントごとに拡張クラスを定義
拡張インスペクタはインスペクタ全体ではなくTransformとか
Cameraとかの各コンポーネント毎にクラスを定義していく。
using UnityEngine; // 必須
using UnityEditor; // 必須
[CustomEditor(typeof(Transform))] // Transformクラス用の拡張であることを表すアトリビュート
public sealed class CustomTransformEditor : Editor // Editorクラスを継承する
{
public override void OnInspectorGUI() // インスペクタが再描画されるタイミングで呼ばれる
{
...
8/30
- 9. Extending the Unity Editor
インスペクタを拡張してみよう (2/5)
・どう拡張できるの?
例えば左のようなプロパティを持つコンポーネントを作ったと
する。するとインスペクタでの表示は右のようになる。
using UnityEngine;
public class Hoge : MonoBehaviour
{
public Vector3 velocity;
}
これではVectorの各要素が縦に並んでいて非常に無様である。
しかも初期状態では折りたたまれているし、色々ひどい。
これをTransformの表示のように横に並べたい。
すっきり!
9/30
- 10. Extending the Unity Editor
インスペクタを拡張してみよう (3/5)
・拡張エディタクラスを定義する
エディタのProject直下(フォルダ構成としてはAssets直下)に
Editorという名前のフォルダを作り、そこに新規スクリプトを
配置。MonoBehaviour向けに書かれている内容をEditor向け
に書きなおす。
using UnityEngine; // 必須
using UnityEditor; // 必須
[CustomEditor(typeof(Hoge))] // Hogeクラス用の拡張であることを宣言
public sealed class HogeEditor : Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector(); // とりあえずデフォルトの表示
}
}
10/30
- 11. Extending the Unity Editor
インスペクタを拡張してみよう (4/5)
・拡張エディタクラスを実装する
EditorGUILayoutには、そのものズバリVector3Fieldという
パーツが用意されているのでそれを利用。
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(Hoge))]
public sealed class HogeEditor : Editor
{
public override void OnInspectorGUI()
{
// 今インスペクタで表示しているオブジェクトのインスタンスがtargetに格納されている
Hoge hoge = target as Hoge; // targetプロパティはObject型なので目的の型にキャスト
Vector3 v = EditorGUILayout.Vector3Field("Velocity", hoge.velocity);
if (v != hoge.velocity) // 変更があった場合のみ処理する
{
Undo.RegisterUndo(hoge, "Velocity Change"); // アンドゥバッファに登録
hoge.velocity = v;
EditorUtility.SetDirty(target); // アセットデータベースに変更を通知
}
}
}
11/30
- 12. Extending the Unity Editor
インスペクタを拡張してみよう (5/5)
・拡張エディタクラスを堪能する
Unityにフォーカスを戻してコンパイルエラーなどがなければ、
インスペクタの表示が以下のように変化している。
Undo.RegisterUndo()して
あるので、Ctrl+Zで取り消し、
Ctrl+Yでやり直しもバッチリ。
12/30
- 13. Extending the Unity Editor
もっと拡張してみよう (1/2)
・リセット・コピー&ペーストの実装
先ほどのプロパティをゼロリセットするボタンと、数値を別の
オブジェクトにコピー&ペーストするボタンを付けてみる。
static Vector3 copyBuffer = Vector3.zero;
public override void OnInspectorGUI()
{
Hoge hoge = target as Hoge;
Vector3 v = EditorGUILayout.Vector3Field("Velocity", hoge.velocity);
EditorGUILayout.BeginHorizontal(); // 水平方向に並べる
if (GUILayout.Button("Reset", EditorStyles.miniButton)) // 見た目にこだわってみる
v = Vector3.zero;
GUILayout.Space(GUILayoutUtility.GetAspectRect(1).width / 3);
if (GUILayout.Button("Copy", EditorStyles.miniButtonLeft))
copyBuffer = v;
if (GUILayout.Button("Paste", EditorStyles.miniButtonRight))
v = copyBuffer;
EditorGUILayout.EndHorizontal();
if (v != hoge.velocity)
{
Undo.RegisterUndo(hoge, "Velocity Change");
hoge.velocity = v;
EditorUtility.SetDirty(target);
}
}
13/30
- 14. Extending the Unity Editor
もっと拡張してみよう (2/2)
・挙動の確認
Unityにフォーカスを戻してコンパイルエラーなどがなければ、
インスペクタの表示が以下のように変化している。
EditorStylesの指定や空白を
配置したことで、まとまりの
ある見た目にする事ができた。
14/30
- 15. Extending the Unity Editor
他にもこんな便利なパーツが
・範囲指定
EditorGUILayout.MinMaxSlider()
・ゲージ表示
EditorGUI.ProgressBar()
15/30
- 16. Extending the Unity Editor
拡張インスペクタの振る舞い (1/2)
・必要な時にnewされている
先ほど実装した拡張インスペクタは、Unityエディタ上で対象の
コンポーネントがアタッチされているオブジェクトが選択され
る度にnewされている。
従って、1つのオブジェクトを選択している間はインスペクタ
自体のメンバプロパティは保持されるが、別のオブジェクトを
選択するとインスペクタのメンバは初期値に戻る。
複数オブジェクトをまたいでやりとりしたい情報がある場合は
staticメンバを利用しよう。
またOnEnable()をオーバーライドすることで、オブジェクト
を選択した瞬間の挙動を定義することも可能。
16/30
- 17. Extending the Unity Editor
拡張インスペクタの振る舞い (2/2)
・必要な時にOnInspectorGUI()が呼ばれる
OnGUI()が毎フレーム呼ばれるのと違い、OnInspectorGUI()
はインスペクターペインの再描画が必要になったタイミングで
随時呼ばれている。
従って「一定間隔で明滅する」といったような、エディタ上で
リアルタイムに何かを動かすような処理は基本的にできない。
(毎回Repaint()を呼びまくれば可能ではある)
・シーンビューの更新時にはOnSceneGUI()
インスペクタの親クラスであるEditorにはOnSceneGUI()と
いうメソッドもあり、これはシーンビューが更新されるときに
呼ばれている。どう使うのかというと…
17/30
- 18. Extending the Unity Editor
シーンビューも拡張してみよう (1/3)
・ハンドル(マニピュレータ)の表示
ベクトルを下図のようにグラフィカルに表示したい。
18/30
- 19. Extending the Unity Editor
シーンビューも拡張してみよう (2/3)
・ハンドル(マニピュレータ)の表示
拡張インスペクタで取り扱っている情報を、シーンビュー上に
グラフィカルに表示すると理解しやすくなる場合がある。
[CustomEditor(typeof(Hoge))]
public sealed class HogeEditor : Editor
{
Hoge hoge = null;
void OnEnable()
{
hoge = target as Hoge;
}
void OnSceneGUI()
{
Vector3 root = hoge.transform.position; // 起点を計算
Vector3 cap = root + hoge.velocity; // 終点を計算
Handles.color = Color.magenta; // 色を変更
Handles.DrawLine(root, cap); // 線を描画
Quaternion rot = Quaternion.LookRotation(hoge.velocity); // 終点の向きを計算
float size = HandleUtility.GetHandleSize(cap); // カメラ距離に依らないサイズを計算
Handles.ArrowCap(0, cap, rot, size); // 矢印型の終点を描画
}
static Vector3 copyBuffer = Vector3.zero;
public override void OnInspectorGUI()
19/30
- 20. Extending the Unity Editor
シーンビューも拡張してみよう (3/3)
・ハンドル(マニピュレータ)の表示
ベクトルがグラフィカルに表示されるようになった。
20/30
- 22. Extending the Unity Editor
ウィンドウを作ってみよう (1/5)
・何を拡張できるの?
Build Settingのようなフローティングウィンドウや、
AnimationウィンドウのようなDock可能なウィンドウ、
あるいは簡単なウィザードなどを独自に作ることができる。
・ウィンドウごとに拡張クラスを定義
カスタムウィンドウは拡張インスペクタと違い、ウィンドウ
1つ1つが独立したインスタンスとして振舞う。
using UnityEngine; // 必須
using UnityEditor; // 必須
public sealed class MaterialReplacer : EditorWindow // EditorWindowクラスを継承する
{
[MenuItem("Tool/Replace Material...")] // エディタのメニューに項目が追加される
static void OpenWindow() // 上記項目をクリックするとこの関数が呼ばれる
{
22/30
- 23. Extending the Unity Editor
ウィンドウを作ってみよう (2/5)
・Floatingウィンドウの場合
public sealed class MaterialReplacer : EditorWindow
{
[MenuItem("Tool/Replace Material...")]
static void OpenWindow() // メニューから呼ばれるためにはstatic関数でなければならない
{
EditorWindow.GetWindow<MaterialReplacer>(true, "Replace Material");
}
}
23/30
- 24. Extending the Unity Editor
ウィンドウを作ってみよう (3/5)
・Dockableウィンドウの場合
public sealed class MaterialReplacer : EditorWindow
{
[MenuItem("Tool/Replace Material...")]
static void OpenWindow() // メニューから呼ばれるためにはstatic関数でなければならない
{
EditorWindow.GetWindow<MaterialReplacer>(false, "Replace Material");
}
}
Dock可能
24/30
- 25. Extending the Unity Editor
ウィンドウを作ってみよう (4/5)
・OnGUI()にレイアウトを実装
拡張インスペクタと同じ要領で、OnGUI()をオーバーライドし
そこへレイアウトを入れこんでいく。
[MenuItem("Tool/Replace Material...")]
static void OpenWindow()
{
EditorWindow.GetWindow<MaterialReplacer>(false, "Replace Material");
}
static Material targetMaterial = null;
void OnGUI()
{
targetMaterial = EditorGUILayout.ObjectField(
"Material", targetMaterial, typeof(Material), false
) as Material;
GUI.enabled = (targetMaterial != null) && (Selection.gameObjects.Length > 0);
if (GUILayout.Button("Replace!"))
{
foreach (var renderer in from go in Selection.gameObjects select go.renderer)
if (renderer != null)
renderer.sharedMaterial = targetMaterial;
}
GUI.enabled = true;
}
25/30
- 26. Extending the Unity Editor
ウィンドウを作ってみよう (5/5)
・ウィンドウが完成
選択中のゲームオブジェクト全てのマテリアルを、指定した
ものに差し替えるツールが完成。
マテリアルを選択し、差し替え
たいオブジェクトを選択状態に
したらReplaceボタンを押す
選択中のオブジェクトにレンダラが付い
ていれば、そのマテリアルが差し替わる
26/30
- 27. Extending the Unity Editor
覚えておきたい (1/4)
・Selectionクラス
エディタ上で選択中のオブジェクトには、Selectionクラスを
通してアクセスできる。
27/30
- 28. Extending the Unity Editor
覚えておきたい (2/4)
・Undoクラス
スクリプトから変更した情報は、何もしないとUndoの履歴に
残らない。エディタの拡張をする場合はUndo.RegisterUndo
を有効に使い、ユーザーの利便性を図ろう。
28/30
- 29. Extending the Unity Editor
覚えておきたい (3/4)
・EditorUtility、FileUtilなどのユーティリティ
EditorUtilityにはファイルオープンダイアログやYes/No形式
のダイアログを表示するためのメソッド、FileUtilにはファイル
のコピーや削除などを行うメソッドなど、色々便利なインター
フェースが用意されている。
・スクリプトから全ての設定にアクセス可能
PlayerSettingsやEditorUserBuildSettingsなどを通して、
エディタ上で触れる設定項目は全てスクリプトからも操作が
可能。面倒な処理はバッチ化してしまえ。
29/30
- 30. Extending the Unity Editor
覚えておきたい (4/4)
・ファイル出力やシステムコールも可能
System.IO.StreamWriterを使ってgmcs.rspを書きだしたり
System.Diagnostics.Processを使ってシステムコールを
呼び出すことも可能。Unity組み込みのWWWクラスを使って
簡単にウェブサーバと連携したり、可能性は無限大。
30/30