3.8K Views
May 08, 18
スライド概要
講演者:安藤 圭吾(ユニティ・テクノロジーズ・ジャパン合同会社)
こんな人におすすめ
・エディター拡張を触っている人
・Unityエディター拡張入門 (http://anchan828.github.io/editor-manual/)の内容が理解できる人
・エディター拡張の最新情報が気になる人
受講者が得られる知見
・エディター拡張の最新機能
リアルタイム3Dコンテンツを制作・運用するための世界的にリードするプラットフォームである「Unity」の日本国内における販売、サポート、コミュニティ活動、研究開発、教育支援を行っています。ゲーム開発者からアーティスト、建築家、自動車デザイナー、映画製作者など、さまざまなクリエイターがUnityを使い想像力を発揮しています。
2018/05/07 - 09 エディター拡張マニアクス2018 安藤 圭吾 ユニティ・テクノロジーズ・ジャパン合同会社
あんどう けいご ユニティ・テクノロジーズ・ジャパン合同会社 / フィールドエンジニア
入門書を書いてました 公開して3年経ちました。Web版無償公開中! 様々な言語で公開されています ・日本語 (公式) ・英語 (Google Translate) ・中国語 (ボランティア) ・韓国語 (ボランティア) 翻訳中らしい! ・ポーランド語 http://anchan828.github.io/editor-manual/
本講演について • ここ3年間の間に実装された機能について話します。 • 質問時間は取れないかもしれません。その場合、講演後の質問ブースやUnityブース、 メール、Twitterなどで質問してください。
Scripted Importer
ファイルとアセットの関係性 インポート ファイル 扱いやすいものに加工 アセット Unityで扱いやすいアセットに 加工された
未対応のファイルを扱うとき インポート ファイル 意味を持たないアセット 扱いやすいものに加工 Prefab / ScriptableObject
Scripted Importer で未対応ファイルに対応する インポート ファイル Prefab アセット / ScriptableObject
インポーターを定義する • ScriptedImporterを継承したクラスを作成する • ScriptedImporterAttributeで対応する拡張子を登録する [ScriptedImporter(1, "cube")] public class CubeImporter : ScriptedImporter { public override void OnImportAsset(AssetImportContext ctx) { // ctx にファイルの情報が格納されている } }
OnImportAssetで行う処理 • メインアセット(とサブアセット)を定義する public override void OnImportAsset(AssetImportContext ctx) { var cube = GameObject.CreatePrimitive(PrimitiveType.Cube); // 最初に追加したものがメインアセットとなる ctx.AddObjectToAsset(”MainAsset", cube); アセットID // サブアセットとしてマテリアルを追加 var material = new Material(Shader.Find("Standard")); ctx.AddObjectToAsset("SubAsset", material); // もし特定のオブジェクトをメインにしたい場合は呼び出す } // ctx.SetMainObject(material);
インポーター設定の表示 • インポーター設定はメインアセットのインスペクターで確認することができる インポーター設定 メインアセットの情報
インポーター設定のプロパティーを追加する • ScriptedImporterクラスにシリアライズ可能なフィールドを追加するだけでOK [ScriptedImporter(1, "cube")] public class CubeImporter : ScriptedImporter { public float m_Scale = 1; } 表示させたくないプロパティーがある
CustomEditorの作成 ScriptedImporterEditor は Editor クラスを継承したもの [CustomEditor(typeof(CubeImporter))] public class CubeImporterEditor : ScriptedImporterEditor { public override void OnInspectorGUI() { // ここでプロパティーを表示するためのGUIを実装 this.ApplyRevertGUI(); } } • ApplyRevertGUIを呼び出すだけで必要 な処理は行われる • Applyボタンを押すとOnImportAssetが 実行される
アセットIDは変更しないこと ctx.AddObjectToAsset(”SubAsset", material); アセットID オブジェクトごとにアセットIDが存在し、metaデータでは fileID として扱われる 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を変更するとどうなる?
アセットIDを変更するとどうなるか
なぜマテリアルの参照が外れたのか アセットIDを変更したことにより fileID の数値も変わった (2100000 -> 2100002) ScriptedImporter: fileIDToRecycleName: 100002: MainAsset/Cube 400002: MainAsset/Cube/Transform 2100000: SubAsset ScriptedImporter: fileIDToRecycleName: 100002: MainAsset/Cube 400002: MainAsset/Cube/Transform 2100002: SubAsset2 スフィアのmetaデータをみると、参照しているマテリアルの fileID は変わっていない m_Materials: - {fileID: 2100000, guid: 7e4b97b7e2d924208879257b687c6928, type: 3}
Unityがサポート済みの拡張子に対しては使用できない たとえば json ファイルに対するインポーターを作成した場合… すでにUnityで対応済みだからインポーターは実装できないとエラーが出る 中身は json でも、独自の拡張子で対応する必要性が出てくる
すでにいくつかのインポーターが実装されている • UI Elements(UXML / USS) • Shader Graph • AlembicImporter • USDImporter
UI Elements
UnityエディターはIMGUIで構築されている ラベル、ボタン、スライダー…はすべてIMGUI
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, "ボタン")) { }
1ヶ所にまとめられてしまうイベントリスナー private void HandleEvents() { switch (Event.current.type) { case EventType.MouseDown: OnMouseDown(); break; case EventType.MouseUp: OnMouseUp(); break; } } case EventType.ContextClick: OnContextClick(); break;
IMGUIではそろそろ限界? Unityでできることが増え、UIも複雑になってきた。 メンテナンス性や生産性、拡張性などあらゆる面で影響が出始めた。 RMGUIであるUIElementsの開発を決意
UI Elementsの現状 • UI Elements はまだ internal customer (Unity社員)向けの機能 • 「Shader Graph」のようなUnity公式プロジェクトで多くのフィードバックを集め ている最中
UI Elementsの概要
UI ElementsはWebの知識があれば理解するのは楽 基本的にはこの4つを覚えればよく、Webの知識に当てはめることができる Visual Tree DOM (HTMLをプログラムで操作する仕組み) UXML HTML USS CSS UQuery Query Selector JQueryもどき
Visual Tree(ビジュアルツリー) ビジュアルツリーは、Visual Element(ビジュアルエレメント)と呼ばれるノードから 成るグラフです。 XML や HTML の事だと思ってください <body> <h1>タイトル</h1> <div> <div>テキスト</div> </div> </body> Visual Element Visual Element Visual Element Visual Element
UXML ビジュアルツリーの構造をテキスト化し、ファイルとして扱うようにしたもの XML や HTML ファイルの事だと思ってください <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>
USS
Visual Elementのスタイルをセレクターで指定するためのもの
CSS の事だと思ってください
.header {
font-size: 20px;
}
Button {
width:200px;
}
<UXML xmlns:ui="UnityEngine.Experimental.UIElements">
<ui:Label class=“header" text="Text"/>
<ui:Button name="ok" text="OK" />
</UXML>
UQuery Visual Element をセレクターで取得するためのもの document.querySelector やJQueryの事だと思ってください // 最初にヒットした hoge クラスを持つボタンを取得 root.Query<Button>(“.hoge”).First(); // hogeクラスを持つ要素配下にあるすべてのボタンを取得 root.Query(“.hoge”).Children<Button>();
UI Elementsを使ってGUIを描画してみる
Visual Container(ビジュアルコンテナ) ビジュアルコンテナは、ビジュアルツリーを格納する領域です。 OnGUI コンテナ RMGUI でいう OnGUI みたいなもの
Visual Container(ビジュアルコンテナ) EditorWindowにOnGUIでラベルを描画する場合 void OnGUI() { var position = new Rect(0,0, 100, 50); EditorGUI.LabelField(position, “ラベル"); }
Visual Container(ビジュアルコンテナ) UIElementsでラベルを描画する場合(その1) void OnEnable() { var label = new Label(“ラベル”); var container = this.GetRootVisualContainer(); container.Add(label); }
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");
UXMLアセットの生成 Createメニューから生成できる プレビューでVisual Elementsを確認できる
UI Elementsの今後 • UI Elements はまだ EditorWindow のみサポート • 2018.3 以降でインスペクターウィンドウのサポートを行う予定 • だが、いろいろ課題が多い • CustomEditor(PreviewGUI含む) • PropertyDrawer • パフォーマンス • 2018.xでは IMGUI にある機能を UIElements に実装する期間です。 • 個人的には、ユーザーがまともに使えるようになるのは 2019.x 後半だと思うのでもう 少し待っててください
Graph View
Graph Viewの現状 • 多くの人が期待している機能だと思う! • Graph View もまだ internal customer (Unity社員)向けの機能 • 「Shader Graph」のようなUnity公式のビジュアルエディターで多くのフィード バックを集めている最中 • みなさんに見せられるものはShader Graphのソースコードくらい • 今後、大きくAPIが変わっていくと思う • ちなみに GUI はすべて UI Elements で実装されています。 • まだドキュメントもないのでこの段階で触るのはオススメしないです • 触れる段階になったら情報を公開していくので待っててください。
Shortcut System
Shift + A というショートカットコマンドを実装するには(その1) • 特定のEditorWindowでショートカットを実装するとき • Eventからショートカットコマンドを検出する 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: … 略 … } }
Shift + A というショートカットコマンドを実装するには(その2) • MenuItemで実装する時 • グローバルなショートカットコマンドを実装できる [MenuItem("UniteTokyo2018/ショートカットを実装 #a")] static void ExecShortcutCommand() { }
難易度の高いショートカット機能の実装 • Eventクラスを駆使して実装するため、難易度が高かった • OS によるキーの違いも考慮しなければいけない • ショートカットキーがコンフリクトしても、それを知るすべがない • ショートカットのキーをユーザーで変更できない! • 唯一、キーを変更できる Preferences -> Keys のショートカット機能も独立した機能 • 実装方法が統一されていないため、ショートカットのリスト化が困難だった
新しいショートカットシステム • 2018.2から新しいショートカットシステムを実装 • まだプレビュー段階 • privateな機能。いずれ public にしてユーザーも実装できるようにする予定 • できるようになること • ショートカットのリスト化(1画面ですべてのショートカットが見れるように) • ユーザーによるショートカットキーの変更 • ショートカットキーがコンフリクトした場合の修正・解決
ショートカットの実装方法 • MenuItemのような実装方法 [Shortcut("MyWindow / MyAction", typeof(MyWindow), "%F")] static void ActionName(ShortcutArguments args) { Debug.Log("ショートカットを実行"); } • ShortcutAttirbuteを使用する • 特定のEditorWindowに対するショートカットの場合は第2引数にTypeを指定 • globalで動作させたい場合はnullを指定 • ショートカットキーのフォーマットはMenuItemと同じ
まだまだ課題はたくさん • 現状サポートしているのはグローバルかEditorWindowのみ • テキストフィールドなどの IMGUI / UIElements に対するショートカットはどうする? • ショートカットキーのコンフリクトは検出できるけど、どうやってユーザーに解決させ よう? • ユーザーは多くのアセットをアセットストアから入手する。 そのぶん、コンフリクトする確率も高くなり、コンフリクトを解決させる頻度が多く なってしまうかもしれない。 • もっとスマートに解決させる方法はないか?
Presets
昔からある Preset Library • カラーパレットや、アニメーションカーブを保存する機能 • 今回紹介するのは、これとは別物です。
Presets Presetオブジェクトに UnityEngine.Object のプロパティーを保存/復元する機能 各コンポーネントにあるPresetボタン 保存ボタンをクリック Presetアセットができる
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
Presetアセットをスクリプトで操作することが可能 // source の SerializedPropertyの値をコピー Preset preset = new Preset(source); // preset をアセットとして保存。拡張子は .preset AssetDatabase.CreateAsset(preset, "Assets/" + name + “.preset"); // targetにコピーした SerializedPropertyの値を流し込む preset.ApplyTo(target);
Presetセレクターをスクリプトで呼び出すことも可能 transform = (Transform) EditorGUILayout.ObjectField(transform, typeof(Transform), true); if (transform && GUILayout.Button("Open Preset Selector")) { PresetSelector.ShowSelector( new Object[] { transform }, null, true); }
Presetでデフォルト値を定義できる • Presetに保存した値をデフォルト値として扱 うことが可能 • Presetアセットのインスペクターにある 「Set as ... Default」ボタンをクリックする ことで有効化 • 新規でオブジェクトを生成するときに、自動 でPresetの値が使用される
Presetで一番役に立つのがインポーター設定 • Presetは UnityEngine.Object に対する値を保存するのでコンポーネントだけではなく、 インポート設定にも対応できる • インポート設定の値はエディター拡張でコードを書かない限り、変更することができな かった。 • Presetにより、コードを書かずにデフォルト値として値を設定できるようになった
Package Manager
Package Manager とは • アセットを配信するシステム 新機能を いち早く届ける 共通の機能を オープンな環境で 楽に共有する Unityをより使いやすく
Module Manager との違い • Module Managerは Unity 4.x 時代から実装されているシステム • コンセプトは Package Manager と同じ(Unityバージョンに影響せずいち早く届ける) • チョット失敗 • 扱うモジュールが core 部分に強く依存していて切り離しが難しかった • それにより、モジュールをアップデートするときにはUnityのバージョンも上げ なければいけないという本末転倒な感じに • 最終的には Module Manager はなくなり、Package Managerに統合される
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"
}
パッケージの構成 • 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
asmdefファイル • スクリプトは アセンブリ定義ファイル を使って管理すること
プロジェクト単位で manifest.json を持つ
• プロジェクト単位で依存するパッケージを指定することができる
• グローバルではUnity公式が持つレジストリを参照するが manifest.json では特定のレ
ジストリURLを指定できる
{
}
"registry": "https://staging-packages.unity.com",
"dependencies": {
"com.unity.package-manager-ui": "1.3.0"
}
レジストリ • 基本はUnity公式が提供するレジストリ • Asset Store も対応予定 • ユーザーがサーバーを立てプライベートレジスト リを使用することも可能 - (時期は未定) • Git やホスティングサービス、Unityコラボレート を指定できるように - (時期は未定)
Package Managerの今後 • アセットストアに対応することでより身近な機能となります • unitypackage は過去のフォーマットとなる • つまり、zipファイルでやり取りする方法は非推奨になる パッケージマネージャーのドキュメントはこちら
Unity C# Reference
標準機能の技術をエディター拡張で使いたい ズーム機能 どうやってズーム機能を実装しているの?
あのプロパティーにアクセスしたい プロパティー名はなんだろう?
情報が足りないことがよくある • ドキュメント化されていない • APIが公開されていない 意図的に公開していない可能性もある それでも、どうにかしたい場合は… DLLをデコンパイルしてコードを見る必要性が出てくる
デコンパイルするのはダメか? • 公式から「ダメ」とは言わないがOKとも言えない • 必要になった人が自己責任でやる分には黙認していた • デコンパイルツールはたくさんあるしその行為を防ぐことは難しい • 現実、多くの人がデコンパイルを行ってより高度なエディター拡張の開発を行っていた • しまいにはGithubに上がっちゃうほど
デコンパイルするのはダメか? • むしろ、エディター拡張においては積極的にデコンパイルしてコードを見ていくべき エディター標準の機能もAPIを使って実装されている どのような機能があるかを把握できる。また、参考にして似た機能が作れる。 • 「コードの設計」「命名規則」は参考になる お作法をUnityに合わせたいという人向け • 「この機能はどうやって動いているんだ?」という疑問を解決し、学ぶべき
公式で C# のソースコードを公開 • デコンパイルしたものよりも正しいコードを読ん でもらう。 • Unity のソースコードは大きく分けて C++ と C# の2種類 • そのうち C# の部分をUnity Reference-Only Licenseの元、公開 • 現在、2017.1 ~ 2018.2 まで公開 UnityCsReference
Unity Reference-Only License とは
参照専用であること 複製は可能 再配布・改変は不可 Clone コピペはダメ
UnityCsReference を有効活用して エディター拡張を学ぶ
まずはUnityCSReferenceをクローンする ① PCにクローンする ② IDEでプロジェクトを開く Clone Projects/CSharp/UnityReferenceSource.sln 下準備が完了
自分にとっての面白機能を探してみる 「プロジェクトブラウザのように領域を変更する機能ってどうやって実現するんだろう」
目的のコードを探す(※慣れが必要) ① 検索機能で目的のコードをみつける ② それっぽい所に目星をつける ProjectBrowser.cs EditorGUIUtility.DrawHorizontalSplitter が怪しいぞ、中身を見てみよう
どのように実装されているか学ぶ 細かく分解していき、理解する
どのように実装されているか学ぶ Splitterの描画方法を知る Repaint以外では 描画しないようにするのか
どのように実装されているか学ぶ Splitterの描画方法を知る GUIの色を変えて、最後に元に戻す。そのために元の色 をキャッシュしておく
どのように実装されているか学ぶ Splitterの描画方法を知る なんと!あの線はテクスチャだったのか! そして、GUI.colorでテクスチャの色も変わるのか!
どのように実装されているか学ぶ Splitterの描画方法は「テクスチャ使って描画する」ということがわかった ではSplitterをマウスでドラッグして領域サイズを変更する方法は… • ページ内検索でdragで検索してみると 引っかかる • HandleHorizontalSplitterといかにも な名前がある という感じで繰り返し調べていく
Unityのソースコードは宝の山 • エディター拡張は公式でも同じ技術を使っているので、コードを眺めるだけでも 学べることはいっぱいある • 「この標準機能はどうやって実装されているんだろう」と興味を持ち、調べるこ とがエディター拡張のスキルを磨く近道
Thank you! ご静聴ありがとうございました
おまけ
Tree View
TreeViewの表示方法は2種類 ヒエラルキーのような表示 テーブルのような表示
TreeViewの構造 • TreeViewItemをツリー状に持つ • 必ずルートとなるTreeViewItemを持つ Root TreeViewItem TreeViewItem TreeViewItem TreeViewItem
Hierarchyウィンドウのようなビューを作成してみる • TreeView を継承したクラスを作成する • BuildRootメソッド内で TreeViewItemのセットアップを行う public class HierarchyView : TreeView { public HierarchyView(TreeViewState state) : base(state) { } } protected override TreeViewItem BuildRoot() { }
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;
}
TreeViewの描画 • EditorWindowで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)); }
MultiColumnHeader • TreeViewにカラムを設けて、テーブルのように扱うことができる • ヘッダーの状態によって表示するTreeViewItemを制御することができる • ソート • 検索 • カラムの表示/非表示 • カラムの表示幅
TreeViewItemが大量でもサクサク • 必要な要素のみを描画するため、要素が5000あってもカクつくことなくサクサク表示できる 描画しない 描画しない
MultiColumnHeaderは少し複雑 • 学ぶことが多く、実装するには少し難易度が高い • マニュアルやサンプルプロジェクト(その1、その2)を参考に学んでください texture-tree-view-sample コメントも細かく追加していきます
Session State
4つ目のデータを保持する方法 保存するデータの種類に合わせ、使い分ける EditorPrefs Unityのバージョンやプロジェクト関係なく グローバルで保持される EditorUserSettings .SetConfigValue Library/EditorUserSettings.asset に保持される ScriptableObject アセットとして保持される SessionState エディターを起動している間のみ保持される
値をファイルに書き込む必要がないものに適している • パスワード/トークンのような個人情報 • スクリプトのコンパイルをまたいで保持したいもの • 現在選択しているタブ • Hierarchyでの開閉状態 • 最後に選択したオブジェクト • 等々… • あまりピンとこない人はUnityCsReferenceで「SessionState」と検索して使用例をみ るといいかもしれない…
Build Report
ビルド情報の詳細データを所得できるようになりました • 今までは 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); } } }
BuildReportで取得できるデータ • ビルド中に発生したログ • 成果物に含まれているファイル(保存先 / ファイルサイズ / アセットの種類) • ビルド前のようなアセット単位ではなくsharedassets0.assets というような、 複数のファイルがまとめられた状態 • ビルド結果(かかった時間、成果物のトータルサイズ、保存先…)
レポートツールを作成するときに便利 CIツール
Pre Process Build
面倒だったビルド直前に処理したいもの • Unity 5.x 時代は、ビルド後に実行されるPostProcessBuildしかなかった • もし、ビルド前に処理したいことがある場合 BuildPipeline.BuildPlayer を使って自分 で Build 機能を実装しなければいけなかった [MenuItem("UniteTokyo2018/BuildPlayer")] static void ExecBuildPlayer() { // ビルド前に処理したいことをする BuildPipeline.BuildPlayer( EditorBuildSettings.scenes, "保存先", BuildTarget.StandaloneOSX, BuildOptions.None); } Build Settings ウィンドウ のビルドボタン封印
ビルド直前に実行されるメソッド • IPreprocessBuildWithReport を継承したクラスを作り OnPreprocessBuild メソッドを 追加することでビルド前に実行されるメソッドを実装できる public class PreProcessBuildSample : IPreprocessBuildWithReport { // 実行される順番を指定できる public int callbackOrder { get { return 0; } } public void OnPreprocessBuild(BuildReport report) { } } ビルドターゲットや保存先などビルドに 必要な情報が格納されている
用途や注意点 • 主に使われそうな例 • ビルドターゲットに合わせてリソースの変更を行う • ResourcesフォルダやStreamingAssetsフォルダの中身を変更する • 注意点 • 非同期処理はできない • どうしてもしたい場合は、従来通りの MenuItem を使って BuildPlayer を呼び出 す手法にする
[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) { }