Successfully reported this slideshow.
Your SlideShare is downloading. ×

Extending the Unity Editor

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Upcoming SlideShare
Unity エディタ拡張
Unity エディタ拡張
Loading in …3
×

Check these out next

1 of 30 Ad

More Related Content

Slideshows for you (20)

Similar to Extending the Unity Editor (20)

Advertisement

Extending the Unity Editor

  1. 1. Extending the Unity Editor Unity Editor の 拡張について
  2. 2. Extending the Unity Editor まずはUnityのGUIを理解しよう (1/2) ・基本的にデバッグ用 毎フレーム1つ1つのコンポーネントについて判定・再描画が 行われるので重い。また使いこなすにはGUISkinなどの細かい 仕様を覚えなくてはならず面倒。 ・Viewport空間ではなくScreen空間に描画 スクリーンに対する絶対座標で描画されるので、画面解像度が 変わった時に破綻する。 ・フォントの問題 PC/Mac以外で日本語文字を表示しようとすると非常に面倒。 携帯機ではDynamicフォントが使えない。 2/30
  3. 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. 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. 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. 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. 7. Extending the Unity Editor EditorGUIとEditorGUILayout (2/2) ・基本的にEditorGUILayoutを使用 ゲーム用のGUIがゲーム画面の左上を基準にして配置されるよ うに、エディタ用のGUIはインスペクタやウィンドウの左上を 基準にして配置されていく。 座標を全部自前で計算してもいいが、EditorGUILayoutで配置 しておくとスケーリングが自動で行われるのでスマート。 ・位置調整用のパーツもある 領域のサイズを考慮して自動的に伸縮するFlexibleSpace()や 逆に勝手に伸縮しないようにするGUILayout.ExpandWidth() を上手く使ってかっこ良く配置しよう。 7/30
  8. 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. 9. Extending the Unity Editor インスペクタを拡張してみよう (2/5) ・どう拡張できるの? 例えば左のようなプロパティを持つコンポーネントを作ったと する。するとインスペクタでの表示は右のようになる。 using UnityEngine; public class Hoge : MonoBehaviour { public Vector3 velocity; } これではVectorの各要素が縦に並んでいて非常に無様である。 しかも初期状態では折りたたまれているし、色々ひどい。 これをTransformの表示のように横に並べたい。 すっきり! 9/30
  10. 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. 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. 12. Extending the Unity Editor インスペクタを拡張してみよう (5/5) ・拡張エディタクラスを堪能する Unityにフォーカスを戻してコンパイルエラーなどがなければ、 インスペクタの表示が以下のように変化している。  Undo.RegisterUndo()して  あるので、Ctrl+Zで取り消し、  Ctrl+Yでやり直しもバッチリ。 12/30
  13. 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. 14. Extending the Unity Editor もっと拡張してみよう (2/2) ・挙動の確認 Unityにフォーカスを戻してコンパイルエラーなどがなければ、 インスペクタの表示が以下のように変化している。  EditorStylesの指定や空白を  配置したことで、まとまりの  ある見た目にする事ができた。 14/30
  15. 15. Extending the Unity Editor 他にもこんな便利なパーツが ・範囲指定 EditorGUILayout.MinMaxSlider() ・ゲージ表示 EditorGUI.ProgressBar() 15/30
  16. 16. Extending the Unity Editor 拡張インスペクタの振る舞い (1/2) ・必要な時にnewされている 先ほど実装した拡張インスペクタは、Unityエディタ上で対象の コンポーネントがアタッチされているオブジェクトが選択され る度にnewされている。 従って、1つのオブジェクトを選択している間はインスペクタ 自体のメンバプロパティは保持されるが、別のオブジェクトを 選択するとインスペクタのメンバは初期値に戻る。 複数オブジェクトをまたいでやりとりしたい情報がある場合は staticメンバを利用しよう。 またOnEnable()をオーバーライドすることで、オブジェクト を選択した瞬間の挙動を定義することも可能。 16/30
  17. 17. Extending the Unity Editor 拡張インスペクタの振る舞い (2/2) ・必要な時にOnInspectorGUI()が呼ばれる OnGUI()が毎フレーム呼ばれるのと違い、OnInspectorGUI() はインスペクターペインの再描画が必要になったタイミングで 随時呼ばれている。 従って「一定間隔で明滅する」といったような、エディタ上で リアルタイムに何かを動かすような処理は基本的にできない。 (毎回Repaint()を呼びまくれば可能ではある) ・シーンビューの更新時にはOnSceneGUI() インスペクタの親クラスであるEditorにはOnSceneGUI()と いうメソッドもあり、これはシーンビューが更新されるときに 呼ばれている。どう使うのかというと… 17/30
  18. 18. Extending the Unity Editor シーンビューも拡張してみよう (1/3) ・ハンドル(マニピュレータ)の表示 ベクトルを下図のようにグラフィカルに表示したい。 18/30
  19. 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. 20. Extending the Unity Editor シーンビューも拡張してみよう (3/3) ・ハンドル(マニピュレータ)の表示 ベクトルがグラフィカルに表示されるようになった。 20/30
  21. 21. Extending the Unity Editor 拡張インスペクタの例 21/30
  22. 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. 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. 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. 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. 26. Extending the Unity Editor ウィンドウを作ってみよう (5/5) ・ウィンドウが完成 選択中のゲームオブジェクト全てのマテリアルを、指定した ものに差し替えるツールが完成。 マテリアルを選択し、差し替え たいオブジェクトを選択状態に したらReplaceボタンを押す 選択中のオブジェクトにレンダラが付い ていれば、そのマテリアルが差し替わる 26/30
  27. 27. Extending the Unity Editor 覚えておきたい (1/4) ・Selectionクラス エディタ上で選択中のオブジェクトには、Selectionクラスを 通してアクセスできる。 27/30
  28. 28. Extending the Unity Editor 覚えておきたい (2/4) ・Undoクラス スクリプトから変更した情報は、何もしないとUndoの履歴に 残らない。エディタの拡張をする場合はUndo.RegisterUndo を有効に使い、ユーザーの利便性を図ろう。 28/30
  29. 29. Extending the Unity Editor 覚えておきたい (3/4) ・EditorUtility、FileUtilなどのユーティリティ EditorUtilityにはファイルオープンダイアログやYes/No形式 のダイアログを表示するためのメソッド、FileUtilにはファイル のコピーや削除などを行うメソッドなど、色々便利なインター フェースが用意されている。 ・スクリプトから全ての設定にアクセス可能 PlayerSettingsやEditorUserBuildSettingsなどを通して、 エディタ上で触れる設定項目は全てスクリプトからも操作が 可能。面倒な処理はバッチ化してしまえ。 29/30
  30. 30. Extending the Unity Editor 覚えておきたい (4/4) ・ファイル出力やシステムコールも可能 System.IO.StreamWriterを使ってgmcs.rspを書きだしたり System.Diagnostics.Processを使ってシステムコールを 呼び出すことも可能。Unity組み込みのWWWクラスを使って 簡単にウェブサーバと連携したり、可能性は無限大。 30/30

×