---
title: 【Unite 2018 Tokyo】エディター拡張マニアクス2018
tags:  #unite 2017 tokyo #unite tokyo 2018 #unitetokyo  
author: [Unity Technologies Japan](https://convert.docswell.com/user/UnityJapan)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/Z5JGM9M5JL.jpg?width=480
description: 講演者：安藤 圭吾(ユニティ・テクノロジーズ・ジャパン合同会社)   こんな人におすすめ  ・エディター拡張を触っている人 ・Unityエディター拡張入門 (http://anchan828.github.io/editor-manual/)の内容が理解できる人 ・エディター拡張の最新情報が気になる人  受講者が得られる知見  ・エディター拡張の最新機能
published: May 08, 18
canonical: https://convert.docswell.com/s/UnityJapan/K14JPK-unite-2018-tokyo2018-96373327
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/Z5JGM9M5JL.jpg)

2018/05/07 - 09
エディター拡張マニアクス2018
安藤 圭吾
ユニティ・テクノロジーズ・ジャパン合同会社


# Page. 2

![Page Image](https://bcdn.docswell.com/page/V47QRWRLJP.jpg)

あんどう けいご
ユニティ・テクノロジーズ・ジャパン合同会社 / フィールドエンジニア


# Page. 3

![Page Image](https://bcdn.docswell.com/page/DKE48K8571.jpg)

入門書を書いてました
公開して3年経ちました。Web版無償公開中！
様々な言語で公開されています
・日本語 (公式)
・英語 (Google Translate)
・中国語 (ボランティア)
・韓国語 (ボランティア)
翻訳中らしい！
・ポーランド語
http://anchan828.github.io/editor-manual/


# Page. 4

![Page Image](https://bcdn.docswell.com/page/PL712X22JG.jpg)

本講演について
• ここ３年間の間に実装された機能について話します。
• 質問時間は取れないかもしれません。その場合、講演後の質問ブースやUnityブース、
メール、Twitterなどで質問してください。


# Page. 5

![Page Image](https://bcdn.docswell.com/page/8G7WRNR2J2.jpg)

Scripted Importer


# Page. 6

![Page Image](https://bcdn.docswell.com/page/W4JZR3R4E3.jpg)

ファイルとアセットの関係性
インポート
ファイル
扱いやすいものに加工
アセット
Unityで扱いやすいアセットに
加工された


# Page. 7

![Page Image](https://bcdn.docswell.com/page/KYE6RXRG7V.jpg)

未対応のファイルを扱うとき
インポート
ファイル
意味を持たないアセット
扱いやすいものに加工
Prefab / ScriptableObject


# Page. 8

![Page Image](https://bcdn.docswell.com/page/5GE58X8XE4.jpg)

Scripted Importer で未対応ファイルに対応する
インポート
ファイル
Prefab
アセット
/ ScriptableObject


# Page. 9

![Page Image](https://bcdn.docswell.com/page/V972KXKQJR.jpg)

インポーターを定義する
• ScriptedImporterを継承したクラスを作成する
• ScriptedImporterAttributeで対応する拡張子を登録する
[ScriptedImporter(1, &quot;cube&quot;)]
public class CubeImporter : ScriptedImporter
{
public override void OnImportAsset(AssetImportContext ctx)
{
// ctx にファイルの情報が格納されている
}
}


# Page. 10

![Page Image](https://bcdn.docswell.com/page/5DJYN8NYEM.jpg)

OnImportAssetで行う処理
• メインアセット（とサブアセット）を定義する
public override void OnImportAsset(AssetImportContext ctx)
{
var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
// 最初に追加したものがメインアセットとなる
ctx.AddObjectToAsset(”MainAsset&quot;, cube);
アセットID
// サブアセットとしてマテリアルを追加
var material = new Material(Shader.Find(&quot;Standard&quot;));
ctx.AddObjectToAsset(&quot;SubAsset&quot;, material);
// もし特定のオブジェクトをメインにしたい場合は呼び出す
}
// ctx.SetMainObject(material);


# Page. 11

![Page Image](https://bcdn.docswell.com/page/2V7NXQXRJ8.jpg)

インポーター設定の表示
• インポーター設定はメインアセットのインスペクターで確認することができる
インポーター設定
メインアセットの情報


# Page. 12

![Page Image](https://bcdn.docswell.com/page/GYJ98D8Z73.jpg)

インポーター設定のプロパティーを追加する
• ScriptedImporterクラスにシリアライズ可能なフィールドを追加するだけでOK
[ScriptedImporter(1, &quot;cube&quot;)]
public class CubeImporter : ScriptedImporter
{
public float m_Scale = 1;
}
表示させたくないプロパティーがある


# Page. 13

![Page Image](https://bcdn.docswell.com/page/5GJ883ZYJD.jpg)

CustomEditorの作成
ScriptedImporterEditor は Editor クラスを継承したもの
[CustomEditor(typeof(CubeImporter))]
public class CubeImporterEditor : ScriptedImporterEditor
{
public override void OnInspectorGUI()
{
// ここでプロパティーを表示するためのGUIを実装
this.ApplyRevertGUI();
}
}
• ApplyRevertGUIを呼び出すだけで必要
な処理は行われる
• Applyボタンを押すとOnImportAssetが
実行される


# Page. 14

![Page Image](https://bcdn.docswell.com/page/MLJLRVD9JR.jpg)

アセットIDは変更しないこと
ctx.AddObjectToAsset(”SubAsset&quot;, 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を変更するとどうなる？


# Page. 15

![Page Image](https://bcdn.docswell.com/page/547MRGPVEW.jpg)

アセットIDを変更するとどうなるか


# Page. 16

![Page Image](https://bcdn.docswell.com/page/ZP7RRZ1W79.jpg)

なぜマテリアルの参照が外れたのか
アセットIDを変更したことにより fileID の数値も変わった (2100000 -&gt; 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}


# Page. 17

![Page Image](https://bcdn.docswell.com/page/NPJXR9YVJX.jpg)

Unityがサポート済みの拡張子に対しては使用できない
たとえば json ファイルに対するインポーターを作成した場合…
すでにUnityで対応済みだからインポーターは実装できないとエラーが出る
中身は json でも、独自の拡張子で対応する必要性が出てくる


# Page. 18

![Page Image](https://bcdn.docswell.com/page/63JKRP35ED.jpg)

すでにいくつかのインポーターが実装されている
• UI Elements（UXML / USS）
• Shader Graph
• AlembicImporter
• USDImporter


# Page. 19

![Page Image](https://bcdn.docswell.com/page/4LE38XG1J5.jpg)

UI Elements


# Page. 20

![Page Image](https://bcdn.docswell.com/page/V8EDM21KEG.jpg)

UnityエディターはIMGUIで構築されている
ラベル、ボタン、スライダー…はすべてIMGUI


# Page. 21

![Page Image](https://bcdn.docswell.com/page/4V7PR1Q3E8.jpg)

UIが複雑になるほど増えていくコード量
大きめのラベルとボタンを追加するだけでもこのコード量
var labelRect = new Rect(0, 0, 100, 50);
var largeFontStyle = new GUIStyle(EditorStyles.label);
largeFontStyle.fontSize = 20;
EditorGUI.LabelField(labelRect, &quot;ラベル&quot;, largeFontStyle);
var buttonRect = new Rect(0, labelRect.height, 100, 50);
if (GUI.Button(buttonRect, &quot;ボタン&quot;))
{
}


# Page. 22

![Page Image](https://bcdn.docswell.com/page/L2JVRY5N7Q.jpg)

1ヶ所にまとめられてしまうイベントリスナー
private void HandleEvents()
{
switch (Event.current.type)
{
case EventType.MouseDown:
OnMouseDown();
break;
case EventType.MouseUp:
OnMouseUp();
break;
}
}
case EventType.ContextClick:
OnContextClick();
break;


# Page. 23

![Page Image](https://bcdn.docswell.com/page/Z5EGM965EL.jpg)

IMGUIではそろそろ限界？
Unityでできることが増え、UIも複雑になってきた。
メンテナンス性や生産性、拡張性などあらゆる面で影響が出始めた。
RMGUIであるUIElementsの開発を決意


# Page. 24

![Page Image](https://bcdn.docswell.com/page/V4JQRW4LEP.jpg)

UI Elementsの現状
• UI Elements はまだ internal customer （Unity社員）向けの機能
• 「Shader Graph」のようなUnity公式プロジェクトで多くのフィードバックを集め
ている最中


# Page. 25

![Page Image](https://bcdn.docswell.com/page/DK748KQ5J1.jpg)

UI Elementsの概要


# Page. 26

![Page Image](https://bcdn.docswell.com/page/PLJ12XP2EG.jpg)

UI ElementsはWebの知識があれば理解するのは楽
基本的にはこの4つを覚えればよく、Webの知識に当てはめることができる
Visual Tree
DOM (HTMLをプログラムで操作する仕組み)
UXML
HTML
USS
CSS
UQuery
Query Selector JQueryもどき


# Page. 27

![Page Image](https://bcdn.docswell.com/page/8GJWRN22E2.jpg)

Visual Tree（ビジュアルツリー）
ビジュアルツリーは、Visual Element（ビジュアルエレメント）と呼ばれるノードから
成るグラフです。
XML や HTML の事だと思ってください
&lt;body&gt;
&lt;h1&gt;タイトル&lt;/h1&gt;
&lt;div&gt;
&lt;div&gt;テキスト&lt;/div&gt;
&lt;/div&gt;
&lt;/body&gt;
Visual Element
Visual Element
Visual Element
Visual Element


# Page. 28

![Page Image](https://bcdn.docswell.com/page/W4EZR3D473.jpg)

UXML
ビジュアルツリーの構造をテキスト化し、ファイルとして扱うようにしたもの
XML や HTML ファイルの事だと思ってください
&lt;UXML xmlns:ui=&quot;UnityEngine.Experimental.UIElements&quot;&gt;
&lt;ui:Label text=&quot;Select something to remove from your suitcase:&quot;/&gt;
&lt;ui:Box&gt;
&lt;ui:Toggle name=&quot;boots&quot; label=&quot;Boots&quot; value=&quot;false&quot; /&gt;
&lt;ui:Toggle name=&quot;helmet&quot; label=&quot;Helmet&quot; value=&quot;false&quot; /&gt;
&lt;ui:Toggle name=&quot;cloak&quot; label=&quot;Cloak of invisibility&quot; value=&quot;false&quot;/&gt;
&lt;/ui:Box&gt;
&lt;ui:Box&gt;
&lt;ui:Button name=&quot;cancel&quot; text=&quot;Cancel&quot; /&gt;
&lt;ui:Button name=&quot;ok&quot; text=&quot;OK&quot; /&gt;
&lt;/ui:Box&gt;
&lt;/UXML&gt;


# Page. 29

![Page Image](https://bcdn.docswell.com/page/KY76RX1GJV.jpg)

USS
Visual Elementのスタイルをセレクターで指定するためのもの
CSS の事だと思ってください
.header {
font-size: 20px;
}
Button {
width:200px;
}
&lt;UXML xmlns:ui=&quot;UnityEngine.Experimental.UIElements&quot;&gt;
&lt;ui:Label class=“header&quot; text=&quot;Text&quot;/&gt;
&lt;ui:Button name=&quot;ok&quot; text=&quot;OK&quot; /&gt;
&lt;/UXML&gt;


# Page. 30

![Page Image](https://bcdn.docswell.com/page/5G758X4X74.jpg)

UQuery
Visual Element をセレクターで取得するためのもの
document.querySelector やJQueryの事だと思ってください
// 最初にヒットした hoge クラスを持つボタンを取得
root.Query&lt;Button&gt;(“.hoge”).First();
// hogeクラスを持つ要素配下にあるすべてのボタンを取得
root.Query(“.hoge”).Children&lt;Button&gt;();


# Page. 31

![Page Image](https://bcdn.docswell.com/page/V9J2KXVQER.jpg)

UI Elementsを使ってGUIを描画してみる


# Page. 32

![Page Image](https://bcdn.docswell.com/page/5DEYN83Y7M.jpg)

Visual Container（ビジュアルコンテナ）
ビジュアルコンテナは、ビジュアルツリーを格納する領域です。
OnGUI
コンテナ
RMGUI でいう OnGUI みたいなもの


# Page. 33

![Page Image](https://bcdn.docswell.com/page/2VJNXQVRE8.jpg)

Visual Container（ビジュアルコンテナ）
EditorWindowにOnGUIでラベルを描画する場合
void OnGUI()
{
var position = new Rect(0,0, 100, 50);
EditorGUI.LabelField(position, “ラベル&quot;);
}


# Page. 34

![Page Image](https://bcdn.docswell.com/page/GYE98D6ZJ3.jpg)

Visual Container（ビジュアルコンテナ）
UIElementsでラベルを描画する場合（その１）
void OnEnable()
{
var label = new Label(“ラベル”);
var container = this.GetRootVisualContainer();
container.Add(label);
}


# Page. 35

![Page Image](https://bcdn.docswell.com/page/5GE883PYED.jpg)

Visual Container（ビジュアルコンテナ）
UIElementsでラベルを描画する場合（その２）
&lt;UXML xmlns:ui=&quot;UnityEngine.Experimental.UIElements&quot;&gt;
&lt;ui:Label text=&quot;ラベル&quot;/&gt;
&lt;/UXML&gt;
var template = AssetDatabase
.LoadAssetAtPath&lt;VisualTreeAsset&gt;(“Assets/SimpleUXML.uxml&quot;);
var container = this.GetRootVisualContainer();
template.CloneTree(container, null);
container.AddStyleSheetPath(“Assets/SimpleUSS.uss&quot;);


# Page. 36

![Page Image](https://bcdn.docswell.com/page/MLELRV19ER.jpg)

UXMLアセットの生成
Createメニューから生成できる
プレビューでVisual Elementsを確認できる


# Page. 37

![Page Image](https://bcdn.docswell.com/page/54JMRGWV7W.jpg)

UI Elementsの今後
• UI Elements はまだ EditorWindow のみサポート
• 2018.3 以降でインスペクターウィンドウのサポートを行う予定
• だが、いろいろ課題が多い
• CustomEditor（PreviewGUI含む）
• PropertyDrawer
• パフォーマンス
• 2018.xでは IMGUI にある機能を UIElements に実装する期間です。
• 個人的には、ユーザーがまともに使えるようになるのは 2019.x 後半だと思うのでもう
少し待っててください


# Page. 38

![Page Image](https://bcdn.docswell.com/page/ZPJRRZYWJ9.jpg)

Graph View


# Page. 39

![Page Image](https://bcdn.docswell.com/page/NPEXR9GVEX.jpg)

Graph Viewの現状
• 多くの人が期待している機能だと思う！
• Graph View もまだ internal customer （Unity社員）向けの機能
• 「Shader Graph」のようなUnity公式のビジュアルエディターで多くのフィード
バックを集めている最中
• みなさんに見せられるものはShader Graphのソースコードくらい
• 今後、大きくAPIが変わっていくと思う
• ちなみに GUI はすべて UI Elements で実装されています。
• まだドキュメントもないのでこの段階で触るのはオススメしないです
• 触れる段階になったら情報を公開していくので待っててください。


# Page. 40

![Page Image](https://bcdn.docswell.com/page/63EKRP457D.jpg)

Shortcut System


# Page. 41

![Page Image](https://bcdn.docswell.com/page/4L738XL1E5.jpg)

Shift + A というショートカットコマンドを実装するには（その１）
• 特定のEditorWindowでショートカットを実装するとき
• Eventからショートカットコマンドを検出する
void OnGUI()
{
var e = Event.current;
switch (e.type)
{
case EventType.MouseDown:
if (e.shift &amp;&amp; e.keyCode == KeyCode.A)
{
// 何か処理
e.Use(); // 最後にEventTypeをUsedに変更
}
break;
case EventType.MouseUp:
… 略 …
}
}


# Page. 42

![Page Image](https://bcdn.docswell.com/page/V87DM2NK7G.jpg)

Shift + A というショートカットコマンドを実装するには（その２）
• MenuItemで実装する時
• グローバルなショートカットコマンドを実装できる
[MenuItem(&quot;UniteTokyo2018/ショートカットを実装 #a&quot;)]
static void ExecShortcutCommand()
{
}


# Page. 43

![Page Image](https://bcdn.docswell.com/page/4VJPR15378.jpg)

難易度の高いショートカット機能の実装
• Eventクラスを駆使して実装するため、難易度が高かった
• OS によるキーの違いも考慮しなければいけない
• ショートカットキーがコンフリクトしても、それを知るすべがない
• ショートカットのキーをユーザーで変更できない！
• 唯一、キーを変更できる Preferences -&gt; Keys のショートカット機能も独立した機能
• 実装方法が統一されていないため、ショートカットのリスト化が困難だった


# Page. 44

![Page Image](https://bcdn.docswell.com/page/L2EVRYPNJQ.jpg)

新しいショートカットシステム
• 2018.2から新しいショートカットシステムを実装
• まだプレビュー段階
• privateな機能。いずれ public にしてユーザーも実装できるようにする予定
• できるようになること
• ショートカットのリスト化（１画面ですべてのショートカットが見れるように）
• ユーザーによるショートカットキーの変更
• ショートカットキーがコンフリクトした場合の修正・解決


# Page. 45

![Page Image](https://bcdn.docswell.com/page/Z57GM9P57L.jpg)

ショートカットの実装方法
• MenuItemのような実装方法
[Shortcut(&quot;MyWindow / MyAction&quot;, typeof(MyWindow), &quot;%F&quot;)]
static void ActionName(ShortcutArguments args)
{
Debug.Log(&quot;ショートカットを実行&quot;);
}
• ShortcutAttirbuteを使用する
• 特定のEditorWindowに対するショートカットの場合は第2引数にTypeを指定
• globalで動作させたい場合はnullを指定
• ショートカットキーのフォーマットはMenuItemと同じ


# Page. 46

![Page Image](https://bcdn.docswell.com/page/V4EQRW2L7P.jpg)

まだまだ課題はたくさん
• 現状サポートしているのはグローバルかEditorWindowのみ
• テキストフィールドなどの IMGUI / UIElements に対するショートカットはどうする？
• ショートカットキーのコンフリクトは検出できるけど、どうやってユーザーに解決させ
よう？
• ユーザーは多くのアセットをアセットストアから入手する。
そのぶん、コンフリクトする確率も高くなり、コンフリクトを解決させる頻度が多く
なってしまうかもしれない。
• もっとスマートに解決させる方法はないか？


# Page. 47

![Page Image](https://bcdn.docswell.com/page/DKJ48KL5E1.jpg)

Presets


# Page. 48

![Page Image](https://bcdn.docswell.com/page/PLE12XL27G.jpg)

昔からある Preset Library
• カラーパレットや、アニメーションカーブを保存する機能
• 今回紹介するのは、これとは別物です。


# Page. 49

![Page Image](https://bcdn.docswell.com/page/8GEWRNV272.jpg)

Presets
Presetオブジェクトに UnityEngine.Object のプロパティーを保存/復元する機能
各コンポーネントにあるPresetボタン
保存ボタンをクリック
Presetアセットができる


# Page. 50

![Page Image](https://bcdn.docswell.com/page/W47ZR344J3.jpg)

Presets
Presetアセットには SerializedPropertyで取得できる値が保存されている
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!181963792 &amp;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


# Page. 51

![Page Image](https://bcdn.docswell.com/page/KYJ6RX3GEV.jpg)

Presetアセットをスクリプトで操作することが可能
// source の SerializedPropertyの値をコピー
Preset preset = new Preset(source);
// preset をアセットとして保存。拡張子は .preset
AssetDatabase.CreateAsset(preset, &quot;Assets/&quot; + name + “.preset&quot;);
// targetにコピーした SerializedPropertyの値を流し込む
preset.ApplyTo(target);


# Page. 52

![Page Image](https://bcdn.docswell.com/page/5GJ58XLXJ4.jpg)

Presetセレクターをスクリプトで呼び出すことも可能
transform = (Transform) EditorGUILayout.ObjectField(transform, typeof(Transform), true);
if (transform &amp;&amp; GUILayout.Button(&quot;Open Preset Selector&quot;))
{
PresetSelector.ShowSelector(
new Object[] { transform }, null, true);
}


# Page. 53

![Page Image](https://bcdn.docswell.com/page/4LE38XL4J5.jpg)

Presetでデフォルト値を定義できる
• Presetに保存した値をデフォルト値として扱
うことが可能
• Presetアセットのインスペクターにある
「Set as ... Default」ボタンをクリックする
ことで有効化
• 新規でオブジェクトを生成するときに、自動
でPresetの値が使用される


# Page. 54

![Page Image](https://bcdn.docswell.com/page/V8EDM2N5EG.jpg)

Presetで一番役に立つのがインポーター設定
• Presetは UnityEngine.Object に対する値を保存するのでコンポーネントだけではなく、
インポート設定にも対応できる
• インポート設定の値はエディター拡張でコードを書かない限り、変更することができな
かった。
• Presetにより、コードを書かずにデフォルト値として値を設定できるようになった


# Page. 55

![Page Image](https://bcdn.docswell.com/page/4V7PR15DE8.jpg)

Package Manager


# Page. 56

![Page Image](https://bcdn.docswell.com/page/L2JVRYPG7Q.jpg)

Package Manager とは
• アセットを配信するシステム
新機能を
いち早く届ける
共通の機能を
オープンな環境で
楽に共有する
Unityをより使いやすく


# Page. 57

![Page Image](https://bcdn.docswell.com/page/Z5EGM9QDEL.jpg)

Module Manager との違い
• Module Managerは Unity 4.x 時代から実装されているシステム
• コンセプトは Package Manager と同じ（Unityバージョンに影響せずいち早く届ける）
• チョット失敗
• 扱うモジュールが core 部分に強く依存していて切り離しが難しかった
• それにより、モジュールをアップデートするときにはUnityのバージョンも上げ
なければいけないという本末転倒な感じに
• 最終的には Module Manager はなくなり、Package Managerに統合される


# Page. 58

![Page Image](https://bcdn.docswell.com/page/V4JQRWKXEP.jpg)

Package Managerの基盤システムはnpm
• パッケージマネージャーはNode.jsのnpmを使用している
• よってパッケージの情報は、package.json で管理する
{
}
&quot;name&quot;: &quot;com.unity.package-4&quot;,
&quot;displayName&quot;: &quot;Package Number 4&quot;,
&quot;version&quot;: &quot;2.5.1&quot;,
&quot;unity&quot;: &quot;2018.1&quot;,
&quot;description&quot;: &quot;This package provides X, Y, and Z.&quot;,
&quot;keywords&quot;: [&quot;key X&quot;, &quot;key Y&quot;, &quot;key Z&quot;],
&quot;category&quot;: &quot;Controllers&quot;,
&quot;dependencies&quot;: {
&quot;com.unity.package-1&quot;: &quot;1.0.0&quot;,
&quot;com.unity.package-2&quot;: &quot;2.0.0&quot;,
&quot;com.unity.package-3&quot;: &quot;3.0.0&quot;
}


# Page. 59

![Page Image](https://bcdn.docswell.com/page/DK748KP2J1.jpg)

パッケージの構成
• README や LICENSE などおなじみの
ファイル構成
• Editor / Runtime / Tests / Samples /
Documentation という特殊なフォルダ
を持つ
• アセットの扱いは普段と変わらない
• パッケージ公開後はread-onlyになる
&lt;root&gt;
├── 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


# Page. 60

![Page Image](https://bcdn.docswell.com/page/PLJ12X9KEG.jpg)

asmdefファイル
• スクリプトは アセンブリ定義ファイル
を使って管理すること


# Page. 61

![Page Image](https://bcdn.docswell.com/page/8GJWRNQPE2.jpg)

プロジェクト単位で manifest.json を持つ
• プロジェクト単位で依存するパッケージを指定することができる
• グローバルではUnity公式が持つレジストリを参照するが manifest.json では特定のレ
ジストリURLを指定できる
{
}
&quot;registry&quot;: &quot;https://staging-packages.unity.com&quot;,
&quot;dependencies&quot;: {
&quot;com.unity.package-manager-ui&quot;: &quot;1.3.0&quot;
}


# Page. 62

![Page Image](https://bcdn.docswell.com/page/W4EZR3W673.jpg)

レジストリ
• 基本はUnity公式が提供するレジストリ
• Asset Store も対応予定
• ユーザーがサーバーを立てプライベートレジスト
リを使用することも可能 - (時期は未定)
• Git やホスティングサービス、Unityコラボレート
を指定できるように - (時期は未定)


# Page. 63

![Page Image](https://bcdn.docswell.com/page/KY76RXGLJV.jpg)

Package Managerの今後
• アセットストアに対応することでより身近な機能となります
• unitypackage は過去のフォーマットとなる
• つまり、zipファイルでやり取りする方法は非推奨になる
パッケージマネージャーのドキュメントはこちら


# Page. 64

![Page Image](https://bcdn.docswell.com/page/5G758XVM74.jpg)

Unity C# Reference


# Page. 65

![Page Image](https://bcdn.docswell.com/page/V9J2KX3RER.jpg)

標準機能の技術をエディター拡張で使いたい
ズーム機能
どうやってズーム機能を実装しているの？


# Page. 66

![Page Image](https://bcdn.docswell.com/page/5DEYN8157M.jpg)

あのプロパティーにアクセスしたい
プロパティー名はなんだろう？


# Page. 67

![Page Image](https://bcdn.docswell.com/page/2VJNXQ24E8.jpg)

情報が足りないことがよくある
• ドキュメント化されていない
• APIが公開されていない
意図的に公開していない可能性もある
それでも、どうにかしたい場合は…
DLLをデコンパイルしてコードを見る必要性が出てくる


# Page. 68

![Page Image](https://bcdn.docswell.com/page/5GE8834VED.jpg)

デコンパイルするのはダメか？
• 公式から「ダメ」とは言わないがOKとも言えない
• 必要になった人が自己責任でやる分には黙認していた
• デコンパイルツールはたくさんあるしその行為を防ぐことは難しい
• 現実、多くの人がデコンパイルを行ってより高度なエディター拡張の開発を行っていた
• しまいにはGithubに上がっちゃうほど


# Page. 69

![Page Image](https://bcdn.docswell.com/page/MLELRV6RER.jpg)

デコンパイルするのはダメか？
• むしろ、エディター拡張においては積極的にデコンパイルしてコードを見ていくべき
エディター標準の機能もAPIを使って実装されている
どのような機能があるかを把握できる。また、参考にして似た機能が作れる。
• 「コードの設計」「命名規則」は参考になる
お作法をUnityに合わせたいという人向け
• 「この機能はどうやって動いているんだ？」という疑問を解決し、学ぶべき


# Page. 70

![Page Image](https://bcdn.docswell.com/page/54JMRG5P7W.jpg)

公式で C# のソースコードを公開
• デコンパイルしたものよりも正しいコードを読ん
でもらう。
• Unity のソースコードは大きく分けて C++ と C#
の2種類
• そのうち C# の部分をUnity Reference-Only
Licenseの元、公開
• 現在、2017.1 ~ 2018.2 まで公開
UnityCsReference


# Page. 71

![Page Image](https://bcdn.docswell.com/page/ZPJRRZ3YJ9.jpg)

Unity Reference-Only License とは


# Page. 72

![Page Image](https://bcdn.docswell.com/page/NPEXR944EX.jpg)

参照専用であること
複製は可能
再配布・改変は不可
Clone
コピペはダメ


# Page. 73

![Page Image](https://bcdn.docswell.com/page/63EKRPZP7D.jpg)

UnityCsReference を有効活用して
エディター拡張を学ぶ


# Page. 74

![Page Image](https://bcdn.docswell.com/page/4L738XD4E5.jpg)

まずはUnityCSReferenceをクローンする
① PCにクローンする
② IDEでプロジェクトを開く
Clone
Projects/CSharp/UnityReferenceSource.sln
下準備が完了


# Page. 75

![Page Image](https://bcdn.docswell.com/page/V87DM2P57G.jpg)

自分にとっての面白機能を探してみる
「プロジェクトブラウザのように領域を変更する機能ってどうやって実現するんだろう」


# Page. 76

![Page Image](https://bcdn.docswell.com/page/4VJPR1ND78.jpg)

目的のコードを探す（※慣れが必要）
① 検索機能で目的のコードをみつける
② それっぽい所に目星をつける
ProjectBrowser.cs
EditorGUIUtility.DrawHorizontalSplitter
が怪しいぞ、中身を見てみよう


# Page. 77

![Page Image](https://bcdn.docswell.com/page/L2EVRYKGJQ.jpg)

どのように実装されているか学ぶ
細かく分解していき、理解する


# Page. 78

![Page Image](https://bcdn.docswell.com/page/Z57GM99D7L.jpg)

どのように実装されているか学ぶ
Splitterの描画方法を知る
Repaint以外では
描画しないようにするのか


# Page. 79

![Page Image](https://bcdn.docswell.com/page/V4EQRWWX7P.jpg)

どのように実装されているか学ぶ
Splitterの描画方法を知る
GUIの色を変えて、最後に元に戻す。そのために元の色
をキャッシュしておく


# Page. 80

![Page Image](https://bcdn.docswell.com/page/DKJ48KK2E1.jpg)

どのように実装されているか学ぶ
Splitterの描画方法を知る
なんと！あの線はテクスチャだったのか！
そして、GUI.colorでテクスチャの色も変わるのか！


# Page. 81

![Page Image](https://bcdn.docswell.com/page/PLE12XXK7G.jpg)

どのように実装されているか学ぶ
Splitterの描画方法は「テクスチャ使って描画する」ということがわかった
ではSplitterをマウスでドラッグして領域サイズを変更する方法は…
• ページ内検索でdragで検索してみると
引っかかる
• HandleHorizontalSplitterといかにも
な名前がある
という感じで繰り返し調べていく


# Page. 82

![Page Image](https://bcdn.docswell.com/page/8GEWRNNP72.jpg)

Unityのソースコードは宝の山
• エディター拡張は公式でも同じ技術を使っているので、コードを眺めるだけでも
学べることはいっぱいある
• 「この標準機能はどうやって実装されているんだろう」と興味を持ち、調べるこ
とがエディター拡張のスキルを磨く近道


# Page. 83

![Page Image](https://bcdn.docswell.com/page/W47ZR336J3.jpg)

Thank you!
ご静聴ありがとうございました


# Page. 84

![Page Image](https://bcdn.docswell.com/page/KYJ6RXXLEV.jpg)

おまけ


# Page. 85

![Page Image](https://bcdn.docswell.com/page/5GJ58XXMJ4.jpg)

Tree View


# Page. 86

![Page Image](https://bcdn.docswell.com/page/V9E2KXXR7R.jpg)

TreeViewの表示方法は2種類
ヒエラルキーのような表示
テーブルのような表示


# Page. 87

![Page Image](https://bcdn.docswell.com/page/5D7YN885JM.jpg)

TreeViewの構造
• TreeViewItemをツリー状に持つ
• 必ずルートとなるTreeViewItemを持つ
Root
TreeViewItem
TreeViewItem
TreeViewItem
TreeViewItem


# Page. 88

![Page Image](https://bcdn.docswell.com/page/2VENXQQ478.jpg)

Hierarchyウィンドウのようなビューを作成してみる
• TreeView を継承したクラスを作成する
• BuildRootメソッド内で TreeViewItemのセットアップを行う
public class HierarchyView : TreeView
{
public HierarchyView(TreeViewState state) : base(state)
{
}
}
protected override TreeViewItem BuildRoot()
{
}


# Page. 89

![Page Image](https://bcdn.docswell.com/page/GY798DD4E3.jpg)

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 = &quot;Animals&quot;,
children = new List&lt;TreeViewItem&gt; {
new TreeViewItem {id = 2, depth = 1, displayName = &quot;Mammals&quot;},
}
}
);
return root;
}


# Page. 90

![Page Image](https://bcdn.docswell.com/page/5G78833V7D.jpg)

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));
}


# Page. 91

![Page Image](https://bcdn.docswell.com/page/ML7LRVVR7R.jpg)

MultiColumnHeader
• TreeViewにカラムを設けて、テーブルのように扱うことができる
• ヘッダーの状態によって表示するTreeViewItemを制御することができる
• ソート
• 検索
• カラムの表示/非表示
• カラムの表示幅


# Page. 92

![Page Image](https://bcdn.docswell.com/page/54EMRGGPJW.jpg)

TreeViewItemが大量でもサクサク
• 必要な要素のみを描画するため、要素が5000あってもカクつくことなくサクサク表示できる
描画しない
描画しない


# Page. 93

![Page Image](https://bcdn.docswell.com/page/ZPERRZZYE9.jpg)

MultiColumnHeaderは少し複雑
• 学ぶことが多く、実装するには少し難易度が高い
• マニュアルやサンプルプロジェクト（その１、その２）を参考に学んでください
texture-tree-view-sample
コメントも細かく追加していきます


# Page. 94

![Page Image](https://bcdn.docswell.com/page/NP7XR9947X.jpg)

Session State


# Page. 95

![Page Image](https://bcdn.docswell.com/page/637KRPPPJD.jpg)

４つ目のデータを保持する方法
保存するデータの種類に合わせ、使い分ける
EditorPrefs
Unityのバージョンやプロジェクト関係なく
グローバルで保持される
EditorUserSettings
.SetConfigValue
Library/EditorUserSettings.asset
に保持される
ScriptableObject
アセットとして保持される
SessionState
エディターを起動している間のみ保持される


# Page. 96

![Page Image](https://bcdn.docswell.com/page/4LJ38XX475.jpg)

値をファイルに書き込む必要がないものに適している
• パスワード/トークンのような個人情報
• スクリプトのコンパイルをまたいで保持したいもの
• 現在選択しているタブ
• Hierarchyでの開閉状態
• 最後に選択したオブジェクト
• 等々…
• あまりピンとこない人はUnityCsReferenceで「SessionState」と検索して使用例をみ
るといいかもしれない…


# Page. 97

![Page Image](https://bcdn.docswell.com/page/V8JDM225JG.jpg)

Build Report


# Page. 98

![Page Image](https://bcdn.docswell.com/page/4VEPR11DJ8.jpg)

ビルド情報の詳細データを所得できるようになりました
• 今までは 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);
}
}
}


# Page. 99

![Page Image](https://bcdn.docswell.com/page/L27VRYYGEQ.jpg)

BuildReportで取得できるデータ
• ビルド中に発生したログ
• 成果物に含まれているファイル（保存先 / ファイルサイズ / アセットの種類）
• ビルド前のようなアセット単位ではなくsharedassets0.assets というような、
複数のファイルがまとめられた状態
• ビルド結果（かかった時間、成果物のトータルサイズ、保存先…)


# Page. 100

![Page Image](https://bcdn.docswell.com/page/Z5JGM9WDJL.jpg)

レポートツールを作成するときに便利
CIツール


# Page. 101

![Page Image](https://bcdn.docswell.com/page/V47QRW3XJP.jpg)

Pre Process Build


# Page. 102

![Page Image](https://bcdn.docswell.com/page/DKE48K1271.jpg)

面倒だったビルド直前に処理したいもの
• Unity 5.x 時代は、ビルド後に実行されるPostProcessBuildしかなかった
• もし、ビルド前に処理したいことがある場合 BuildPipeline.BuildPlayer を使って自分
で Build 機能を実装しなければいけなかった
[MenuItem(&quot;UniteTokyo2018/BuildPlayer&quot;)]
static void ExecBuildPlayer()
{
// ビルド前に処理したいことをする
BuildPipeline.BuildPlayer(
EditorBuildSettings.scenes,
&quot;保存先&quot;,
BuildTarget.StandaloneOSX,
BuildOptions.None);
}
Build Settings ウィンドウ
のビルドボタン封印


# Page. 103

![Page Image](https://bcdn.docswell.com/page/PL712XGKJG.jpg)

ビルド直前に実行されるメソッド
• IPreprocessBuildWithReport を継承したクラスを作り OnPreprocessBuild メソッドを
追加することでビルド前に実行されるメソッドを実装できる
public class PreProcessBuildSample : IPreprocessBuildWithReport
{
// 実行される順番を指定できる
public int callbackOrder { get { return 0; } }
public void OnPreprocessBuild(BuildReport report)
{
}
}
ビルドターゲットや保存先などビルドに
必要な情報が格納されている


# Page. 104

![Page Image](https://bcdn.docswell.com/page/8G7WRNKPJ2.jpg)

用途や注意点
• 主に使われそうな例
• ビルドターゲットに合わせてリソースの変更を行う
• ResourcesフォルダやStreamingAssetsフォルダの中身を変更する
• 注意点
• 非同期処理はできない
• どうしてもしたい場合は、従来通りの MenuItem を使って BuildPlayer を呼び出
す手法にする


# Page. 105

![Page Image](https://bcdn.docswell.com/page/W4JZR3Z6E3.jpg)

[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)
{
}


