Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

【Unite 2018 Tokyo】エディター拡張マニアクス2018

7,791 views

Published on

講演者:安藤 圭吾(ユニティ・テクノロジーズ・ジャパン合同会社)

こんな人におすすめ
・エディター拡張を触っている人
・Unityエディター拡張入門 (http://anchan828.github.io/editor-manual/)の内容が理解できる人
・エディター拡張の最新情報が気になる人

受講者が得られる知見
・エディター拡張の最新機能

Published in: Technology
  • Be the first to comment

【Unite 2018 Tokyo】エディター拡張マニアクス2018

  1. 1. エディター拡張マニアクス2018 ユニティ・テクノロジーズ・ジャパン合同会社 安藤 圭吾 2018/05/07 - 09
  2. 2. あんどう けいご ユニティ・テクノロジーズ・ジャパン合同会社 / フィールドエンジニア
  3. 3. 入門書を書いてました 公開して3年経ちました。Web版無償公開中! 様々な言語で公開されています
 ・日本語 (公式) ・英語 (Google Translate) ・中国語 (ボランティア) ・韓国語 (ボランティア) 
 翻訳中らしい! ・ポーランド語 http://anchan828.github.io/editor-manual/
  4. 4. 本講演について • ここ3年間の間に実装された機能について話します。
 • 質問時間は取れないかもしれません。その場合、講演後の質問ブースやUnityブース、 メール、Twitterなどで質問してください。
  5. 5. Scripted Importer
  6. 6. ファイルとアセットの関係性 ファイル アセット インポート Unityで扱いやすいアセットに 加工された 扱いやすいものに加工
  7. 7. 未対応のファイルを扱うとき Prefab / ScriptableObject 扱いやすいものに加工 ファイル 意味を持たないアセット インポート
  8. 8. Scripted Importer で未対応ファイルに対応する ファイル アセットPrefab / ScriptableObject インポート
  9. 9. インポーターを定義する [ScriptedImporter(1, "cube")] public class CubeImporter : ScriptedImporter { public override void OnImportAsset(AssetImportContext ctx) { // ctx にファイルの情報が格納されている } } • ScriptedImporterを継承したクラスを作成する • ScriptedImporterAttributeで対応する拡張子を登録する
  10. 10. OnImportAssetで行う処理 • メインアセット(とサブアセット)を定義する public override void OnImportAsset(AssetImportContext ctx) { var cube = GameObject.CreatePrimitive(PrimitiveType.Cube); // 最初に追加したものがメインアセットとなる ctx.AddObjectToAsset(”MainAsset", cube); // サブアセットとしてマテリアルを追加 var material = new Material(Shader.Find("Standard")); ctx.AddObjectToAsset("SubAsset", material); // もし特定のオブジェクトをメインにしたい場合は呼び出す // ctx.SetMainObject(material); } アセットID
  11. 11. インポーター設定の表示 • インポーター設定はメインアセットのインスペクターで確認することができる インポーター設定 メインアセットの情報
  12. 12. インポーター設定のプロパティーを追加する [ScriptedImporter(1, "cube")] public class CubeImporter : ScriptedImporter { public float m_Scale = 1; } • ScriptedImporterクラスにシリアライズ可能なフィールドを追加するだけでOK 表示させたくないプロパティーがある
  13. 13. CustomEditorの作成 [CustomEditor(typeof(CubeImporter))] public class CubeImporterEditor : ScriptedImporterEditor { public override void OnInspectorGUI() { // ここでプロパティーを表示するためのGUIを実装         this.ApplyRevertGUI(); } } ScriptedImporterEditor は Editor クラスを継承したもの • ApplyRevertGUIを呼び出すだけで必要 な処理は行われる • Applyボタンを押すとOnImportAssetが 実行される
  14. 14. アセットIDは変更しないこと ctx.AddObjectToAsset(”SubAsset", material); アセットID fileFormatVersion: 2 guid: 7e4b97b7e2d924208879257b687c6928 ScriptedImporter: fileIDToRecycleName: 100002: MainAsset/Cube 400002: MainAsset/Cube/Transform 2100000: SubAsset 2300002: MainAsset/Cube/MeshRenderer 3300002: MainAsset/Cube/MeshFilter 6500002: MainAsset/Cube/BoxCollider 100100002: MainAsset オブジェクトごとにアセットIDが存在し、metaデータでは fileID として扱われる では、アセットIDを変更するとどうなる?
  15. 15. アセットIDを変更するとどうなるか
  16. 16. なぜマテリアルの参照が外れたのか ScriptedImporter: fileIDToRecycleName: 100002: MainAsset/Cube 400002: MainAsset/Cube/Transform 2100000: SubAsset ScriptedImporter: fileIDToRecycleName: 100002: MainAsset/Cube 400002: MainAsset/Cube/Transform 2100002: SubAsset2 アセットIDを変更したことにより fileID の数値も変わった (2100000 -> 2100002) m_Materials: - {fileID: 2100000, guid: 7e4b97b7e2d924208879257b687c6928, type: 3} スフィアのmetaデータをみると、参照しているマテリアルの fileID は変わっていない
  17. 17. Unityがサポート済みの拡張子に対しては使用できない たとえば json ファイルに対するインポーターを作成した場合… すでにUnityで対応済みだからインポーターは実装できないとエラーが出る 中身は json でも、独自の拡張子で対応する必要性が出てくる
  18. 18. すでにいくつかのインポーターが実装されている • UI Elements(UXML / USS) • Shader Graph • AlembicImporter • USDImporter
  19. 19. UI Elements
  20. 20. UnityエディターはIMGUIで構築されている ラベル、ボタン、スライダー…はすべてIMGUI
  21. 21. UIが複雑になるほど増えていくコード量 var labelRect = new Rect(0, 0, 100, 50); var largeFontStyle = new GUIStyle(EditorStyles.label); largeFontStyle.fontSize = 20; EditorGUI.LabelField(labelRect, "ラベル", largeFontStyle); var buttonRect = new Rect(0, labelRect.height, 100, 50); if (GUI.Button(buttonRect, "ボタン")) { } 大きめのラベルとボタンを追加するだけでもこのコード量
  22. 22. 1ヶ所にまとめられてしまうイベントリスナー private void HandleEvents() { switch (Event.current.type) { case EventType.MouseDown: OnMouseDown(); break; case EventType.MouseUp: OnMouseUp(); break; case EventType.ContextClick: OnContextClick(); break; } }
  23. 23. IMGUIではそろそろ限界? Unityでできることが増え、UIも複雑になってきた。 メンテナンス性や生産性、拡張性などあらゆる面で影響が出始めた。 RMGUIであるUIElementsの開発を決意
  24. 24. UI Elementsの現状 • UI Elements はまだ internal customer (Unity社員)向けの機能 • 「Shader Graph」のようなUnity公式プロジェクトで多くのフィードバックを集め ている最中
  25. 25. UI Elementsの概要
  26. 26. UI ElementsはWebの知識があれば理解するのは楽 基本的にはこの4つを覚えればよく、Webの知識に当てはめることができる Visual Tree DOM (HTMLをプログラムで操作する仕組み) UXML HTML USS CSS UQuery Query Selector JQueryもどき
  27. 27. Visual Tree(ビジュアルツリー) ビジュアルツリーは、Visual Element(ビジュアルエレメント)と呼ばれるノードから 成るグラフです。 Visual Element Visual Element Visual Element Visual Element <body> <h1>タイトル</h1> <div> <div>テキスト</div> </div> </body> XML や HTML の事だと思ってください
  28. 28. UXML ビジュアルツリーの構造をテキスト化し、ファイルとして扱うようにしたもの <UXML xmlns:ui="UnityEngine.Experimental.UIElements"> <ui:Label text="Select something to remove from your suitcase:"/> <ui:Box> <ui:Toggle name="boots" label="Boots" value="false" /> <ui:Toggle name="helmet" label="Helmet" value="false" /> <ui:Toggle name="cloak" label="Cloak of invisibility" value="false"/> </ui:Box> <ui:Box> <ui:Button name="cancel" text="Cancel" /> <ui:Button name="ok" text="OK" /> </ui:Box> </UXML> XML や HTML ファイルの事だと思ってください
  29. 29. USS Visual Elementのスタイルをセレクターで指定するためのもの .header { font-size: 20px; } Button { width:200px; } CSS の事だと思ってください <UXML xmlns:ui="UnityEngine.Experimental.UIElements"> <ui:Label class=“header" text="Text"/> <ui:Button name="ok" text="OK" /> </UXML>
  30. 30. UQuery Visual Element をセレクターで取得するためのもの // 最初にヒットした hoge クラスを持つボタンを取得 root.Query<Button>(“.hoge”).First(); // hogeクラスを持つ要素配下にあるすべてのボタンを取得 root.Query(“.hoge”).Children<Button>(); document.querySelector やJQueryの事だと思ってください
  31. 31. UI Elementsを使ってGUIを描画してみる
  32. 32. Visual Container(ビジュアルコンテナ) ビジュアルコンテナは、ビジュアルツリーを格納する領域です。 RMGUI でいう OnGUI みたいなもの OnGUI コンテナ
  33. 33. void OnGUI() { var position = new Rect(0,0, 100, 50); EditorGUI.LabelField(position, “ラベル"); } Visual Container(ビジュアルコンテナ) EditorWindowにOnGUIでラベルを描画する場合
  34. 34. Visual Container(ビジュアルコンテナ) UIElementsでラベルを描画する場合(その1) void OnEnable() { var label = new Label(“ラベル”); var container = this.GetRootVisualContainer(); container.Add(label); }
  35. 35. Visual Container(ビジュアルコンテナ) UIElementsでラベルを描画する場合(その2) <UXML xmlns:ui="UnityEngine.Experimental.UIElements"> <ui:Label text="ラベル"/> </UXML> var template = AssetDatabase            .LoadAssetAtPath<VisualTreeAsset>(“Assets/SimpleUXML.uxml"); var container = this.GetRootVisualContainer(); template.CloneTree(container, null); container.AddStyleSheetPath(“Assets/SimpleUSS.uss");
  36. 36. UXMLアセットの生成 プレビューでVisual Elementsを確認できるCreateメニューから生成できる
  37. 37. UI Elementsの今後 • UI Elements はまだ EditorWindow のみサポート • 2018.3 以降でインスペクターウィンドウのサポートを行う予定 • だが、いろいろ課題が多い • CustomEditor(PreviewGUI含む) • PropertyDrawer • パフォーマンス • 2018.xでは IMGUI にある機能を UIElements に実装する期間です。 • 個人的には、ユーザーがまともに使えるようになるのは 2019.x 後半だと思うのでもう 少し待っててください
  38. 38. Graph View
  39. 39. Graph Viewの現状 • 多くの人が期待している機能だと思う! • Graph View もまだ internal customer (Unity社員)向けの機能 • 「Shader Graph」のようなUnity公式のビジュアルエディターで多くのフィード バックを集めている最中 • みなさんに見せられるものはShader Graphのソースコードくらい • 今後、大きくAPIが変わっていくと思う • ちなみに GUI はすべて UI Elements で実装されています。 • まだドキュメントもないのでこの段階で触るのはオススメしないです • 触れる段階になったら情報を公開していくので待っててください。
  40. 40. Shortcut System
  41. 41. Shift + A というショートカットコマンドを実装するには(その1) void OnGUI() { var e = Event.current; switch (e.type) { case EventType.MouseDown: if (e.shift && e.keyCode == KeyCode.A) { // 何か処理 e.Use(); // 最後にEventTypeをUsedに変更 } break; case EventType.MouseUp: … 略 … } } • 特定のEditorWindowでショートカットを実装するとき • Eventからショートカットコマンドを検出する
  42. 42. Shift + A というショートカットコマンドを実装するには(その2) [MenuItem("UniteTokyo2018/ショートカットを実装 #a")] static void ExecShortcutCommand() { } • MenuItemで実装する時 • グローバルなショートカットコマンドを実装できる
  43. 43. 難易度の高いショートカット機能の実装 • Eventクラスを駆使して実装するため、難易度が高かった • OS によるキーの違いも考慮しなければいけない
 • ショートカットキーがコンフリクトしても、それを知るすべがない
 • ショートカットのキーをユーザーで変更できない!
 • 唯一、キーを変更できる Preferences -> Keys のショートカット機能も独立した機能 • 実装方法が統一されていないため、ショートカットのリスト化が困難だった
  44. 44. 新しいショートカットシステム • 2018.2から新しいショートカットシステムを実装 • まだプレビュー段階 • privateな機能。いずれ public にしてユーザーも実装できるようにする予定 • できるようになること • ショートカットのリスト化(1画面ですべてのショートカットが見れるように) • ユーザーによるショートカットキーの変更 • ショートカットキーがコンフリクトした場合の修正・解決
  45. 45. ショートカットの実装方法 • MenuItemのような実装方法 [Shortcut("MyWindow / MyAction", typeof(MyWindow), "%F")] static void ActionName(ShortcutArguments args) { Debug.Log("ショートカットを実行"); } • ShortcutAttirbuteを使用する • 特定のEditorWindowに対するショートカットの場合は第2引数にTypeを指定 • globalで動作させたい場合はnullを指定 • ショートカットキーのフォーマットはMenuItemと同じ
  46. 46. まだまだ課題はたくさん • 現状サポートしているのはグローバルかEditorWindowのみ • テキストフィールドなどの IMGUI / UIElements に対するショートカットはどうする? • ショートカットキーのコンフリクトは検出できるけど、どうやってユーザーに解決させ よう? • ユーザーは多くのアセットをアセットストアから入手する。
 そのぶん、コンフリクトする確率も高くなり、コンフリクトを解決させる頻度が多く なってしまうかもしれない。 • もっとスマートに解決させる方法はないか?
  47. 47. Presets
  48. 48. 昔からある Preset Library • カラーパレットや、アニメーションカーブを保存する機能 • 今回紹介するのは、これとは別物です。
  49. 49. Presets Presetオブジェクトに UnityEngine.Object のプロパティーを保存/復元する機能 Presetアセットができる保存ボタンをクリック 各コンポーネントにあるPresetボタン
  50. 50. Presets Presetアセットには SerializedPropertyで取得できる値が保存されている %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!181963792 &2655988077585873504 Preset: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInternal: {fileID: 0} m_Name: Camera m_TargetType: m_NativeTypeID: 20 m_ManagedTypePPtr: {fileID: 0} m_ManagedTypeFallback: m_Properties: - target: {fileID: 0} propertyPath: m_Enabled value: 1 objectReference: {fileID: 0} - target: {fileID: 0} propertyPath: m_ClearFlags value: 1 objectReference: {fileID: 0} - target: {fileID: 0} propertyPath: m_BackGroundColor.r value: 0.19215687 objectReference: {fileID: 0} SerializedPropertyを
 保持していると思えばOK
  51. 51. Presetアセットをスクリプトで操作することが可能 // source の SerializedPropertyの値をコピー Preset preset = new Preset(source); // preset をアセットとして保存。拡張子は .preset AssetDatabase.CreateAsset(preset, "Assets/" + name + “.preset"); // targetにコピーした SerializedPropertyの値を流し込む preset.ApplyTo(target);
  52. 52. Presetセレクターをスクリプトで呼び出すことも可能 transform = (Transform) EditorGUILayout.ObjectField(transform, typeof(Transform), true); if (transform && GUILayout.Button("Open Preset Selector")) { PresetSelector.ShowSelector(         new Object[] { transform }, null, true); }
  53. 53. Presetでデフォルト値を定義できる • Presetに保存した値をデフォルト値として扱 うことが可能 • Presetアセットのインスペクターにある 「Set as ... Default」ボタンをクリックする ことで有効化 • 新規でオブジェクトを生成するときに、自動 でPresetの値が使用される
  54. 54. Presetで一番役に立つのがインポーター設定 • Presetは UnityEngine.Object に対する値を保存するのでコンポーネントだけではなく、 インポート設定にも対応できる • インポート設定の値はエディター拡張でコードを書かない限り、変更することができな かった。 • Presetにより、コードを書かずにデフォルト値として値を設定できるようになった
  55. 55. Package Manager
  56. 56. Package Manager とは • アセットを配信するシステム 新機能を
 いち早く届ける 共通の機能を
 楽に共有する オープンな環境で Unityをより使いやすく
  57. 57. Module Manager との違い • Module Managerは Unity 4.x 時代から実装されているシステム • コンセプトは Package Manager と同じ(Unityバージョンに影響せずいち早く届ける) • チョット失敗 • 扱うモジュールが core 部分に強く依存していて切り離しが難しかった • それにより、モジュールをアップデートするときにはUnityのバージョンも上げ なければいけないという本末転倒な感じに
 • 最終的には Module Manager はなくなり、Package Managerに統合される
  58. 58. Package Managerの基盤システムはnpm • パッケージマネージャーはNode.jsのnpmを使用している • よってパッケージの情報は、package.json で管理する { "name": "com.unity.package-4", "displayName": "Package Number 4", "version": "2.5.1", "unity": "2018.1", "description": "This package provides X, Y, and Z.", "keywords": ["key X", "key Y", "key Z"], "category": "Controllers", "dependencies": { "com.unity.package-1": "1.0.0", "com.unity.package-2": "2.0.0", "com.unity.package-3": "3.0.0" } }
  59. 59. パッケージの構成 • README や LICENSE などおなじみの ファイル構成 • Editor / Runtime / Tests / Samples / Documentation という特殊なフォルダ を持つ • アセットの扱いは普段と変わらない • パッケージ公開後はread-onlyになる <root> ├── package.json ├── README.md ├── CHANGELOG.md ├── LICENSE.md ├── QAReport.md ├── Editor │ ├── Unity.[YourPackageName].Editor.asmdef │ └── EditorExample.cs ├── Runtime │ ├── Unity.[YourPackageName].asmdef │ └── RuntimeExample.cs ├── Tests │ ├── Editor │ │ ├── Unity.[YourPackageName].Editor.Tests.asmdef │ │ └── EditorExampleTest.cs │ └── Runtime │ ├── Unity.[YourPackageName].Tests.asmdef │ └── RuntimeExampleTest.cs ├── Samples │ └── SampleExample.cs └── Documentation ├── your-package-name.md └── Images
  60. 60. asmdefファイル • スクリプトは アセンブリ定義ファイル を使って管理すること
  61. 61. プロジェクト単位で manifest.json を持つ • プロジェクト単位で依存するパッケージを指定することができる • グローバルではUnity公式が持つレジストリを参照するが manifest.json では特定のレ ジストリURLを指定できる { "registry": "https://staging-packages.unity.com", "dependencies": { "com.unity.package-manager-ui": "1.3.0" } }
  62. 62. レジストリ • 基本はUnity公式が提供するレジストリ • Asset Store も対応予定 • ユーザーがサーバーを立てプライベートレジスト リを使用することも可能 - (時期は未定) • Git やホスティングサービス、Unityコラボレート を指定できるように - (時期は未定)
  63. 63. Package Managerの今後 • アセットストアに対応することでより身近な機能となります • unitypackage は過去のフォーマットとなる • つまり、zipファイルでやり取りする方法は非推奨になる パッケージマネージャーのドキュメントはこちら
  64. 64. Unity C# Reference
  65. 65. 標準機能の技術をエディター拡張で使いたい どうやってズーム機能を実装しているの? ズーム機能
  66. 66. あのプロパティーにアクセスしたい プロパティー名はなんだろう?
  67. 67. 情報が足りないことがよくある • ドキュメント化されていない • APIが公開されていない 意図的に公開していない可能性もある DLLをデコンパイルしてコードを見る必要性が出てくる それでも、どうにかしたい場合は…
  68. 68. デコンパイルするのはダメか? • 公式から「ダメ」とは言わないがOKとも言えない • 必要になった人が自己責任でやる分には黙認していた • デコンパイルツールはたくさんあるしその行為を防ぐことは難しい • 現実、多くの人がデコンパイルを行ってより高度なエディター拡張の開発を行っていた • しまいにはGithubに上がっちゃうほど
  69. 69. • むしろ、エディター拡張においては積極的にデコンパイルしてコードを見ていくべき • 「コードの設計」「命名規則」は参考になる • 「この機能はどうやって動いているんだ?」という疑問を解決し、学ぶべき デコンパイルするのはダメか? エディター標準の機能もAPIを使って実装されている どのような機能があるかを把握できる。また、参考にして似た機能が作れる。 お作法をUnityに合わせたいという人向け
  70. 70. 公式で C# のソースコードを公開 • デコンパイルしたものよりも正しいコードを読ん でもらう。 • Unity のソースコードは大きく分けて C++ と C# の2種類 • そのうち C# の部分をUnity Reference-Only Licenseの元、公開 • 現在、2017.1 ~ 2018.2 まで公開 UnityCsReference
  71. 71. Unity Reference-Only License とは
  72. 72. 参照専用であること 複製は可能 再配布・改変は不可 Clone コピペはダメ
  73. 73. UnityCsReference を有効活用して エディター拡張を学ぶ
  74. 74. まずはUnityCSReferenceをクローンする Clone ① PCにクローンする ② IDEでプロジェクトを開く Projects/CSharp/UnityReferenceSource.sln 下準備が完了
  75. 75. 自分にとっての面白機能を探してみる 「プロジェクトブラウザのように領域を変更する機能ってどうやって実現するんだろう」
  76. 76. 目的のコードを探す(※慣れが必要) ① 検索機能で目的のコードをみつける ② それっぽい所に目星をつける EditorGUIUtility.DrawHorizontalSplitter 
 が怪しいぞ、中身を見てみよう ProjectBrowser.cs
  77. 77. どのように実装されているか学ぶ 細かく分解していき、理解する
  78. 78. どのように実装されているか学ぶ Repaint以外では 描画しないようにするのか Splitterの描画方法を知る
  79. 79. どのように実装されているか学ぶ GUIの色を変えて、最後に元に戻す。そのために元の色 をキャッシュしておく Splitterの描画方法を知る
  80. 80. どのように実装されているか学ぶ なんと!あの線はテクスチャだったのか! そして、GUI.colorでテクスチャの色も変わるのか! Splitterの描画方法を知る
  81. 81. どのように実装されているか学ぶ Splitterの描画方法は「テクスチャ使って描画する」ということがわかった ではSplitterをマウスでドラッグして領域サイズを変更する方法は… • ページ内検索でdragで検索してみると 引っかかる • HandleHorizontalSplitterといかにも な名前がある という感じで繰り返し調べていく
  82. 82. Unityのソースコードは宝の山 • エディター拡張は公式でも同じ技術を使っているので、コードを眺めるだけでも 学べることはいっぱいある • 「この標準機能はどうやって実装されているんだろう」と興味を持ち、調べるこ とがエディター拡張のスキルを磨く近道
  83. 83. Thank you! ご静聴ありがとうございました
  84. 84. おまけ
  85. 85. Tree View
  86. 86. TreeViewの表示方法は2種類 ヒエラルキーのような表示 テーブルのような表示
  87. 87. TreeViewの構造 TreeViewItem TreeViewItem TreeViewItem TreeViewItem Root • TreeViewItemをツリー状に持つ • 必ずルートとなるTreeViewItemを持つ
  88. 88. Hierarchyウィンドウのようなビューを作成してみる public class HierarchyView : TreeView { public HierarchyView(TreeViewState state) : base(state) { } protected override TreeViewItem BuildRoot() { } } • TreeView を継承したクラスを作成する • BuildRootメソッド内で TreeViewItemのセットアップを行う
  89. 89. TreeViewItemの構築 protected override TreeViewItem BuildRoot() { // Root の depth は -1 var root = new TreeViewItem { id = 0, depth = -1 };     // AddChild メソッド、または children プロパティで開閉で表示可能な子要素を追加 root.AddChild( new TreeViewItem { id = 1, depth = 0, displayName = "Animals", children = new List<TreeViewItem> { new TreeViewItem {id = 2, depth = 1, displayName = "Mammals"}, } } ); return root; }
  90. 90. TreeViewの描画 private HierarchyView hierarchyView; // スクリプトをコンパイルしてもステートが維持できるように [SerializeField] private TreeViewState state; void OnEnable() { // 開閉状態や、どのTreeViewItemを // 選択しているかなどの状態が格納されるstateオブジェクト if (state == null) { state = new TreeViewState(); } hierarchyView = new HierarchyView(state);     // 初期化のために呼び出す hierarchyView.Reload(); } void OnGUI() { hierarchyView.OnGUI(new Rect(Vector2.zero, position.size)); } • EditorWindowでTreeViewを描画する
  91. 91. MultiColumnHeader • TreeViewにカラムを設けて、テーブルのように扱うことができる • ヘッダーの状態によって表示するTreeViewItemを制御することができる • ソート • 検索 • カラムの表示/非表示 • カラムの表示幅
  92. 92. TreeViewItemが大量でもサクサク • 必要な要素のみを描画するため、要素が5000あってもカクつくことなくサクサク表示できる 描画しない 描画しない
  93. 93. MultiColumnHeaderは少し複雑 • 学ぶことが多く、実装するには少し難易度が高い • マニュアルやサンプルプロジェクト(その1、その2)を参考に学んでください texture-tree-view-sample コメントも細かく追加していきます
  94. 94. Session State
  95. 95. 4つ目のデータを保持する方法 保存するデータの種類に合わせ、使い分ける EditorPrefs Unityのバージョンやプロジェクト関係なく
 グローバルで保持される EditorUserSettings
 .SetConfigValue Library/EditorUserSettings.asset に保持される ScriptableObject アセットとして保持される SessionState エディターを起動している間のみ保持される
  96. 96. 値をファイルに書き込む必要がないものに適している • パスワード/トークンのような個人情報 • スクリプトのコンパイルをまたいで保持したいもの • 現在選択しているタブ • Hierarchyでの開閉状態 • 最後に選択したオブジェクト • 等々… • あまりピンとこない人はUnityCsReferenceで「SessionState」と検索して使用例をみ るといいかもしれない…
  97. 97. Build Report
  98. 98. ビルド情報の詳細データを所得できるようになりました • 今までは Editor.log などを解析してデータを取得しなければいけなかった • BuildPipeline.BuildPlayer の戻り値として取得できる • PostProcessBuildの引数として取得できる public void OnPostprocessBuild(BuildReport report) { foreach (var step in report.steps) { foreach (var message in step.messages) { // スクリプトが Missing などの警告メッセージも取得できる Debug.Log(message.content); } } }
  99. 99. BuildReportで取得できるデータ • ビルド中に発生したログ • 成果物に含まれているファイル(保存先 / ファイルサイズ / アセットの種類) • ビルド前のようなアセット単位ではなくsharedassets0.assets というような、
 複数のファイルがまとめられた状態 • ビルド結果(かかった時間、成果物のトータルサイズ、保存先…)
  100. 100. レポートツールを作成するときに便利 CIツール
  101. 101. Pre Process Build
  102. 102. 面倒だったビルド直前に処理したいもの • Unity 5.x 時代は、ビルド後に実行されるPostProcessBuildしかなかった • もし、ビルド前に処理したいことがある場合 BuildPipeline.BuildPlayer を使って自分 で Build 機能を実装しなければいけなかった [MenuItem("UniteTokyo2018/BuildPlayer")] static void ExecBuildPlayer() { // ビルド前に処理したいことをする BuildPipeline.BuildPlayer( EditorBuildSettings.scenes, "保存先", BuildTarget.StandaloneOSX, BuildOptions.None); } Build Settings ウィンドウ のビルドボタン封印
  103. 103. ビルド直前に実行されるメソッド • IPreprocessBuildWithReport を継承したクラスを作り OnPreprocessBuild メソッドを 追加することでビルド前に実行されるメソッドを実装できる public class PreProcessBuildSample : IPreprocessBuildWithReport { // 実行される順番を指定できる public int callbackOrder { get { return 0; } } public void OnPreprocessBuild(BuildReport report) { } } ビルドターゲットや保存先などビルドに 必要な情報が格納されている
  104. 104. 用途や注意点 • 主に使われそうな例 • ビルドターゲットに合わせてリソースの変更を行う • ResourcesフォルダやStreamingAssetsフォルダの中身を変更する • 注意点 • 非同期処理はできない • どうしてもしたい場合は、従来通りの MenuItem を使って BuildPlayer を呼び出 す手法にする
  105. 105. [Tips] Callbacks系はすべて interface となりました • Attributeで指定する方法から、クラスを作成する方法へと変わりました [PostProcessBuild(1)] public static void OnPostprocessBuild(BuildTarget buildTarget, string path) { } public class PostProcessBuildSample : IPostprocessBuildWithReport { public int callbackOrder { get { return 1; } } public void OnPostprocessBuild(BuildReport report) { } } • 引数によって得られる情報も格段に増えているので新しい方法で実装するのを推奨

×