UIElements+UI BuilderでEditor拡張を作ろう

1.6K Views

November 29, 19

スライド概要

Unity2019.1から正式になったUIElements、Package ManagerからリリースされたUI Builder。
この二つを組み合わせる事で、Editor拡張のワークフローは大きく改善されて生産的になります。

この講演では、新しくなったEditor拡張の作り方について、紹介していきます。

profile-image

リアルタイム3Dコンテンツを制作・運用するための世界的にリードするプラットフォームである「Unity」の日本国内における販売、サポート、コミュニティ活動、研究開発、教育支援を行っています。ゲーム開発者からアーティスト、建築家、自動車デザイナー、映画製作者など、さまざまなクリエイターがUnityを使い想像力を発揮しています。

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

ダウンロード

関連スライド

各ページのテキスト
1.

UIElements+UI BuilderでEditor拡張を作ろう ユニティ・テクノロジーズ・ジャパン合同会社 黒河 優介

2.

お分かりいただけただろうか? このスライドはEditor拡張です 本日紹介するUIElements/UI Builderで作成しました https://github.com/wotakuro/UnityDojo201911

3.

本日のアジェンダ - UIElements/UIBuilderの紹介 - UIElementsのツリー構造について - UIElementsの応用編

4.
[beta]
従来のEditor拡張
class MyWindow : EditorWindow
{
[MenuItem("Tools/Sample")]
public static void Create()
{
EditorWindow.GetWindow<MyWindow>();
}
// 描画はC#で全部書く
void OnGUI()
{
EditorGUILayout.LabelField("テキスト表示");
if (GUILayout.Button("決定"))
{
// 何か処理する
}
}
5.

従来のEditor拡張の問題点 - 全体の構成、表示スタイル、ロジックが分離されていない - 全てC#に集約されてしまっている - パフォーマンス上も問題があった (描画毎にレイアウト処理が必要なため)

6.

UIElementsが出来ました! 2019.1でExperimentalが外れました

7.

UIElementsについて - 新しいUIシステム - Editor拡張やランタイムでも同等に動く(予定) - Webに近い設計(uxml/uss/C#) - パフォーマンスも改善

8.

UIElementsを構成する三つの要素 - 構造を表すUXML - スタイルを表すUSS - ロジックのC#

9.
[beta]
UXMLサンプル
<ui:UXML xmlns:ui="UnityEngine.UIElements"
xmlns:uie="UnityEditor.UIElements">
<ui:Label text="テキスト表示" class="myclass"
style="color: rgb(255, 0, 0);">
<Style src="NewUI.uss" />
</ui:Label>
<ui:Button name="SubmitBtn"
text="決定" class="mybutton">
<Style src="NewUI.uss" />
</ui:Button>
</ui:UXML>
10.

USSサンプル .myclass { font-size: 30px; } .mybutton { font-size: 20px; position: absolute; bottom: 3px; right: 3px; }

11.
[beta]
CSサンプル
using UnityEditor;
using UnityEngine.UIElements;
public class NewUICs : EditorWindow
{
[MenuItem("Tools/NewUI")]
public static void Create()
{
EditorWindow.GetWindow<NewUICs>();
}
void OnEnable()
{
// uxml 読み込み
string path = "Assets/Editor/Samples/001/NewUI.uxml";
var asset =
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(path);
// 読み込んだUXMLをEditorWindowに配置
asset.CloneTree(this.rootVisualElement);
}
12.

実行画面 NewUICs テキスト表示 決定

13.

それぞれの関係について

14.
[beta]
実行画面とXMLの関係性について
実行画面
テキスト表示
決定
UXML
<ui:UXML xmlns:ui="UnityEngine.UIElements"
xmlns:uie="UnityEditor.UIElements">
<ui:Label text="テキスト表示" class="myclass"
style="color: rgb(255, 0, 0);">
<Style src="NewUI.uss" />
</ui:Label>
<ui:Button name="SubmitBtn"
text="決定" class="mybutton">
<Style src="NewUI.uss" />
</ui:Button>
</ui:UXML>
UIのパーツは、XMLの要素で表します。
XMLなので階層構造も記述できます。
15.
[beta]
UXMLとUSSの関係性について
UXML
<ui:UXML xmlns:ui="UnityEngine.UIElements"
xmlns:uie="UnityEditor.UIElements">
<ui:Label text="テキスト表示" class="myclass"
style="color: rgb(255, 0, 0);">
<Style src="NewUI.uss" />
</ui:Label>
<ui:Button name="SubmitBtn"
text="決定" class="mybutton">
<Style src="NewUI.uss" />
</ui:Button>
</ui:UXML>
USS
.myclass {
font-size: 30px;
}
.mybutton {
font-size: 20px;
position: absolute;
bottom: 3px;
right: 3px;
}
表示するフォント等をussに記述して共通
化出来ます。classという形で参照します
ussがなくても、uxmlのみでもフォント等の指定が出来ます。
表示スタイルと構造を分離するためにUSSがあります。
16.
[beta]
全体図について
C#
void OnEnable()
{
// uxml 読み込み
string path = "Assets/Editor/Samples/001/NewUI.uxml";
var asset =
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(path);
// 読み込んだUXMLをEditorWindowに配置
asset.CloneTree(this.rootVisualElement);
}
UXMLを読み込みます
EditorWindowに配置します
実行画面
テキスト表示
決定
UXML
<ui:UXML xmlns:ui="UnityEngine.UIElements"
xmlns:uie="UnityEditor.UIElements">
<ui:Label text="テキスト表示" class="myclass"
style="color: rgb(255, 0, 0);">
<Style src="NewUI.uss" />
</ui:Label>
<ui:Button name="SubmitBtn"
text="決定" class="mybutton">
<Style src="NewUI.uss" />
</ui:Button>
</ui:UXML>
USS
.myclass {
font-size: 30px;
}
.mybutton {
font-size: 20px;
position: absolute;
bottom: 3px;
right: 3px;
}
参照してスタイ
ルを適用します
17.
[beta]
えっ、このUXMLとUSS書くんですか...
<ui:UXML xmlns:ui="UnityEngine.UIElements"
xmlns:uie="UnityEditor.UIElements">
<ui:Label text="テキスト表示" class="myclass"
style="color: rgb(255, 0, 0);">
<Style src="NewUI.uss" />
</ui:Label>
<ui:Button name="SubmitBtn"
text="決定" class="mybutton">
<Style src="NewUI.uss" />
</ui:Button>
</ui:UXML>
.myclass {
font-size: 30px;
}
.mybutton {
font-size: 20px;
position: absolute;
bottom: 3px;
right: 3px;
}
時代は令和なのに、
こんなのを手打ちだなんてあんまりです…。
18.

そこでUI Builderですよ! UI Builder Explorer ▼ StyleSheet .myclass .mybutton Add new selector... State Class ▼ Hierarchy Label .unity-text-element .unity-label .my Button #SubmitBtn .unity-text-element .ui Library ▼ Unity VisualElement Button Scroller Toggle Label Text Field Object Field Foldout ▶ Numeric Fields ▶ Value Fields ▶ Choice Fields ▶ Containers Viewport - NewUI.uxml* - UI Builder 0.8.3-preview File Runtime Theme Preview テキスト表示 決定 Inspector ▼ Button Name SubmitBtn View Data Key Picking Mode Position Tooltip Usage Hints None Tabindex 0 Focusable Binding Path Text 決定 ▼ StyleSheet ▼ Style Class List Add Style Class to List Extract Inlined Styles to New Class .unity-text-element x .unity-button x .mybutton x ▼ Matching Selectors ▶ .unity-button ▶ .unity-button ▶ .mybutton ▼ Inlined Styles ▼ Display Opacity 100 Display UXML Preview USS Preview

19.

UIBuilderについて - UIElementsのためのオーサリングツール - uxml/ussを作成するためのツール - PackageManagerよりpreiewで提供中 複数選択できなかったり色々と文句はありますが… XML手書きじゃないので大分改善します

20.

これからのEditor拡張について - レイアウト、スタイル、ロジックが分離 - レイアウト、スタイルはUIBuilderで編集

21.

ところで...... UIの画面と実際のデータ連動どうするの? ボタンを押したときの処理とか… 表示されるテキストの書き換えとか…

22.

UIElementsのツリー構造について UIElementsのツリー構造を利用してC#と紐づけます まずは、UIElementsのツリー構造について理解しましょう

23.

XMLを読み込んだ時に... <ui:VisualElement name="Screen"> <ui:TextField name="TextInput" /> <ui:Button text="追加する" name="AddBtn" /> <ui:ScrollView name="ScrollList" view-data-key="ListItem"> <ui:Button text="削除する" name="DelBtn" /> </ui:ScrollView> </ui:VisualElement>

24.

内部ではParseしてTree構造に変換して保持 <ui:VisualElement name="Screen"> <ui:TextField name="TextInput" /> <ui:Button text="追加する" name="AddBtn" /> <ui:ScrollView name="ScrollList" view-data-key="ListItem"> <ui:Button text="削除する" name="DelBtn" /> </ui:ScrollView> </ui:VisualElement> VisualElement name : Screen TextField name : TextInput Button name : AddBtn text:追加する ScrollView name : ScrollList Button name : DelBtn text:削除する

25.

C#でTree構造を操作して表示を変えます VisualElement name : Screen TextField name : TextInput Button name : AddBtn text:追加する ScrollView name : ScrollList Button name : DelBtn text:削除する Treeからノードを取得してきて、その ノードを操作することで表示を変えま す。 例えば「text」を書き換える事で表示 するテキストを変えたり… Buttonの場合、Clickイベントを追加し て、ボタン推したときのコールバック を追加したり…

26.

表示物の追加・削除も... VisualElement name : Screen TextField name : TextInput Button name : AddBtn text:追加する ScrollView name : ScrollList Button name : DelBtn text:削除する Label class:myitem text:1行目 Label class:myitem text:2行目 ツリー構造を直接操作 して、表示の変更を行 います

27.

Tree構造...... VisualElement name : Screen TextField name : TextInput Button name : AddBtn text:追加する ScrollView name : ScrollList Button name : DelBtn text:削除する 普段見ないような形だし… 難しそうなんですが……

28.

少し変形してみましょう VisualElement name : Screen TextField name : TextInput Button name : AddBtn text:追加する ScrollView name : ScrollList Button name : DelBtn text:削除する VisualElement name : Screen TextField name : TextInput Button name : AddBtn text:追加する ScrollView name : ScrollList Button name : DelBtn text:削除する

29.

ついでに見た目も変えてみましょうか... VisualElement name : Screen TextField name : TextInput Button name : AddBtn text:追加する ScrollView name : ScrollList Button name : DelBtn text:削除する ▼ Hierarchy ▼ VisualElement #Screen ▶ TextField #TextInput Button #AddBtn ▼ ScrollView #ScrollList ▼ VisualElement #unity-content-viewport ▼ VisualElement #unity-content-container Button #DelBtn

30.

Hierarchy操作じゃん! ▼ Hierarchy ▼ VisualElement #Screen ▶ TextField #TextInput Button #AddBtn ▼ ScrollView #ScrollList ▼ VisualElement #unity-content-viewport ▼ VisualElement #unity-content-container Button #DelBtn なるほど!完全理解! Hierarchyからオブジェクトをしてきて書き換える! GameObjectで散々やってきましたね!

31.

つまるところ・・・ ▼ Hierarchy ▼ VisualElement #Screen ▶ TextField #TextInput Button #AddBtn ▼ ScrollView #ScrollList ▼ VisualElement #unity-content-viewport ▼ VisualElement #unity-content-container Button #DelBtn GameObject->VisualElement Componentではなくて、VisualElementを継承 したクラス群(Label,Button等)

32.

ボタンをおして処理が走るのを追ってみましょう InteractSample 追加要素です 追加する 削除する InteractSample 追加要素です 追加する 削除する 追加要素です

33.

UXMLはこんな感じになっています rootVisualElement VisualElement name : Screen TextField name : TextInput Button name : AddBtn text:追加する ScrollView name : ScrollList Button name : DelBtn text:削除する UXML <ui:VisualElement name="Screen"> <ui:TextField name="TextInput" /> <ui:Button text="追加する" name="AddBtn" /> <ui:ScrollView name="ScrollList" view-data-key="ListItem"> <ui:Button text="削除する" name="DelBtn" /> </ui:ScrollView> </ui:VisualElement>

34.

2つのステップに分解してみます 1.ボタンと処理の紐づけ 2.押したときに要素を追加する処理

35.
[beta]
ボタンと処理の紐づけ
実行画面
InteractSample
追加する
削除する
C#
// uxml 読み込み
string path = "Assets/Editor/Samples/002/InteractSample.uxml";
var asset =
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(path);
// 読み込んだUXMLをEditorWindowに配置
asset.CloneTree(this.rootVisualElement);
// EditorWindow下にある「AddBtn」という
// 名前のボタンに対して処理をします
rootVisualElement.Query<Button>("AddBtn").
ForEach(button) => {
//ボタンクリック時にOnClickAddBtnを呼び出します
button.clicked += this.OnClickAddBtn;
});
rootVisualElement
VisualElement
name : Screen
TextField
name : TextInput
Button
name : AddBtn
text:追加する
ScrollView
name : ScrollList
Button
name : DelBtn
text:削除する
36.
[beta]
ボタンと処理の紐づけ(1)
rootVisualElement.Query<Button>("AddBtn").
ForEach((button) => {
button.clicked += this.OnClickAddBtn;
});
rootVisualElement
VisualElement
name : Screen
TextField
name : TextInput
Button
name : AddBtn
text:追加する
ScrollView
name : ScrollList
Button
name : DelBtn
text:削除する
EditorWindow側から
rootVisualElementを取得して…
37.
[beta]
ボタンと処理の紐づけ(2)
rootVisualElement.Query<Button>("AddBtn").
ForEach((button) => {
button.clicked += this.OnClickAddBtn;
});
rootVisualElement
VisualElement
name : Screen
TextField
name : TextInput
Button
name : AddBtn
text:追加する
ScrollView
name : ScrollList
Button
name : DelBtn
text:削除する
rootVisualElement以下で
「AddBtn」というnameの
ButtonをQueryを出します
(要求します)
38.
[beta]
ボタンと処理の紐づけ(3)
rootVisualElement.Query<Button>("AddBtn").
ForEach((button) => {
button.clicked += this.OnClickAddBtn;
});
rootVisualElement
VisualElement
name : Screen
TextField
name : TextInput
Button
name : AddBtn
text:追加する
ScrollView
name : ScrollList
Button
name : DelBtn
text:削除する
Queryで見つかった要素
全てに対して…
39.
[beta]
ボタンと処理の紐づけ(4)
rootVisualElement.Query<Button>("AddBtn").
ForEach((button) => {
button.clicked += this.OnClickAddBtn;
});
rootVisualElement
VisualElement
name : Screen
TextField
name : TextInput
Button
name : AddBtn
text:追加する
ScrollView
name : ScrollList
Button
name : DelBtn
text:削除する
Foreachの引数に渡したい処理を
lambda式で記述しています。
ここでは、見つかったButton全て
のclickedイベントに処理を追加
していきます
40.

押したときに要素を追加する手順 InteractSample 追加要素です 追加する 削除する 1.TextFieldから文 字列を取得して 2.Labelを生成 3.生成したLabelを ScrollViewに追加 します

41.

押したときに要素を追加する手順 rootVisualElement VisualElement name : Screen TextField name : TextInput Button name : AddBtn text:追加する ScrollView name : ScrollList Button name : DelBtn text:削除する 1.TextFieldから文字列を 取得して Label text:追加要素です class:myitem 2. Labelを生成 3.ScrollViewに Labelを追加する

42.

実行したらこうなって欲しい rootVisualElement VisualElement name : Screen TextField name : TextInput Button name : AddBtn text:追加する ScrollView name : ScrollList Button name : DelBtn text:削除する Label text:追加要素です class:myitem InteractSample 追加要素です 追加する 削除する 追加要素です

43.
[beta]
// 追加するボタンが押されたときに呼び出されます
void OnClickAddBtn()
{
// TextField
TextField textFiled =
rootVisualElement.Query<TextField>().AtIndex(0);
// Labelを動的に生成します
var newLineLabel = new Label(textFiled.value);
// 削除時にQueryで見つけるようにmyitemクラスを追加します
newLineLabel.AddToClassList("myitem");
// 「ScrollList」という名前のScrollViewに
// 生成したLabelを追加します
rootVisualElement.Query<ScrollView>("ScrollList").
AtIndex(0).Add(newLineLabel);
}
44.
[beta]
// 追加するボタンが押されたときに呼び出されます
void OnClickAddBtn()
{
// TextField
TextField textFiled =
rootVisualElement.Query<TextField>().AtIndex(0);
// Labelを動的に生成します
var newLineLabel = new Label(textFiled.value);
// 削除時にQueryで見つけるようにmyitemクラスを追加します
newLineLabel.AddToClassList("myitem");
// 「ScrollList」という名前のScrollViewに
// 生成したLabelを追加します
rootVisualElement.Query<ScrollView>("ScrollList").
AtIndex(0).Add(newLineLabel);
}
1.TextFieldから文字列を
取得して
45.
[beta]
// 追加するボタンが押されたときに呼び出されます
void OnClickAddBtn()
{
// TextField
TextField textFiled =
rootVisualElement.Query<TextField>().AtIndex(0);
// Labelを動的に生成します
var newLineLabel = new Label(textFiled.value);
// 削除時にQueryで見つけるようにmyitemクラスを追加します
newLineLabel.AddToClassList("myitem");
// 「ScrollList」という名前のScrollViewに
// 生成したLabelを追加します
rootVisualElement.Query<ScrollView>("ScrollList").
AtIndex(0).Add(newLineLabel);
}
2. Labelを生成
46.
[beta]
// 追加するボタンが押されたときに呼び出されます
void OnClickAddBtn()
{
// TextField
TextField textFiled =
rootVisualElement.Query<TextField>().AtIndex(0);
// Labelを動的に生成します
var newLineLabel = new Label(textFiled.value);
// 削除時にQueryで見つけるようにmyitemクラスを追加します
newLineLabel.AddToClassList("myitem");
// 「ScrollList」という名前のScrollViewに
// 生成したLabelを追加します
rootVisualElement.Query<ScrollView>("ScrollList").
AtIndex(0).Add(newLineLabel);
}
3.ScrollViewに
Labelを追加する
47.

「ボタンを押して追加する」が出来ました! InteractSample 追加要素です 追加する 削除する InteractSample 追加要素です 追加する 削除する 追加要素です

48.

削除も同じように... InteractSample 2行目 追加する 削除する 1行目 2行目 2行目を削除したい VisualElement name : Screen TextField name : TextInput Button name : AddBtn text:追加する ScrollView name : ScrollList Button name : DelBtn text:削除する Label text:1行目 class:myitem Label text:2行目 class:myitem

49.
[beta]
// ScrollViewを取得します
ScrollView scrollView =
rootVisualElement.Query<ScrollView>("ScrollList").
AtIndex(0);
//ScrollViewの中にあるmyitemを持つモノを探してきて…
//最後の要素をピックアップします
var element =
scrollView.Query<VisualElement>(null,"myitem").Last();
// 要素があるようなら…
if (element != null)
{
// 該当要素をparentからRemoveして削除します
element.parent.Remove(element);
}
50.

削除も出来たので完成です

51.
[beta]
コードについて補足(1)
// 追加するボタンが押されたときに呼び出されます
void OnClickAddBtn()
{
// TextField
TextField textFiled =
rootVisualElement.Query<TextField>().AtIndex(0);
// Labelを動的に生成します
var newLineLabel = new Label(textFiled.value);
// 削除時にQueryで見つけるようにmyitemクラスを追加します
newLineLabel.AddToClassList("myitem");
// 「ScrollList」という名前のScrollViewに
// 生成したLabelを追加します
rootVisualElement.Query<ScrollView>("ScrollList").
AtIndex(0).Add(newLineLabel);
}
引数なしなので、名前指定なしで
TextField一覧取得になります
52.
[beta]
コードについて補足(2)
// 追加するボタンが押されたときに呼び出されます
void OnClickAddBtn()
{
// TextField
TextField textFiled =
rootVisualElement.Query<TextField>().AtIndex(0);
// Labelを動的に生成します
var newLineLabel = new Label(textFiled.value);
// 削除時にQueryで見つけるようにmyitemクラスを追加します
newLineLabel.AddToClassList("myitem");
// 「ScrollList」という名前のScrollViewに
// 生成したLabelを追加します
rootVisualElement.Query<ScrollView>("ScrollList").
AtIndex(0).Add(newLineLabel);
}
Queryで見つかった中で何番目の
オブジェクトのみ処理という事
もAtIndexで出来ます
53.
[beta]
コードについて補足(3)
// ScrollViewを取得します
ScrollView scrollView =
rootVisualElement.Query<ScrollView>("ScrollList").
AtIndex(0);
//ScrollViewの中にあるmyitemを持つモノを探してきて…
//最後の要素をピックアップします
var element =
scrollView.Query<VisualElement>(null,"myitem").Last();
// 要素があるようなら…
if (element != null)
{
// 該当要素をparentからRemoveして削除します
element.parent.Remove(element);
}
第二引数では、クラスを指定します。
ここではmyitemクラスを持つ
Elementを探してきます
54.

Queryの観点からみたnameとclass Elementは一つのnameのみ Elementは複数のclassを持てる たとえばfadein/fadeoutする等、不特定多数の要素に対して 一括でコントロールしたいものとかはclassでやっておく

55.

UIElements応用編 - UIElementDebugger - IMGUIと混ぜて利用する - CustomPropertyについて - 自前のElementを作る

56.

UIElementsDebugger呼び出し Window Help Next Window Ctrl+Tab Previous Window Ctrl+Shift+Tab Layouts > Asset Store Ctrl+9 Package Manager Asset Management > TextMeshPro > General > Rendering > Animation > Audio > Sequencing > Analysis > 2D > AI > XR > UI > Profiler Ctrl+7 Frame Debugger Physics Debugger UIElements Debugger IMGUI Debugger Alt+5 Window->Analysis UIElements Debugger から呼び出し可能

57.

UIElementsDebugger UIElements Debugger InteractSample Pick Element Show Layout ▼ VisualElement #unity-panel-container523 IMGUIContainer #Dockarea23 .unity-imgui-container ▼ VisualElement #rootVisualContainer30 ▼ VisualElement #Screen ▼ TextField #TextInput .unity-base-field .unity-base-field-... TextInput #unity-text-input .unity-base-text-field__inpu Button #AddBtn .unity-text-element .unity-button ▼ ScrollView #ScrollList .unity-scroll-view .unity-scroll-v ▼ VisualElement #unity-content-viewport .unity-scroll-view ▼ VisualElement #unity-content-container Button #DelBtn .unity-text-element .unity-button Label .unity-text-element .unity-label .myitem Label .unity-text-element .unity-label .myitem ▶ Scroller .unity-scroller .unity-scroller--horizontal .un ▶ Scroller .unity-scroller .unity-scroller--vertical .unit margin 0 border 0 padding 0 1 283 x 14 0 0 0 0 ▶ UXML Dump Stylesheets DefaultCommonDark_inter.uss \Assets\Editor\Samples\002\InteractSample.us Matching Selectors ▶ .unity- DefaultCommonDark_inter.uss:106 Element styles Name Debug Id 132129 Text 2行目 Picking Mode Position Pseudo States None Focusable False InteractSample 2行目 追加する 削除する 1行目 2行目

58.

UIElementsDebugger - Editor上のUIElementsのデバッグ用ツールです - 実行中の構造が閲覧・編集できます - 既存Editor拡張の解析・開発のおともに

59.
[beta]
IMGUIと混ぜて利用する
昔作ったプログラムと
組み合わせで作ることは出来ないのでしょうか?
class MyWindow : EditorWindow
{
[MenuItem("Tools/Sample")]
public static void Create()
{
EditorWindow.GetWindow<MyWindow>();
}
// 描画はC#で全部書く
void OnGUI()
{
EditorGUILayout.LabelField("テキスト表示");
if (GUILayout.Button("決定"))
{
// 何か処理する
}
}
60.

IMGUIContainerあります! UnityEditor本体でも使ってます

61.

早速実行してみました IMGUIContainerSample IMGUIのコンテンツ(0) IMGUIのコンテンツ(1) IMGUIのコンテンツ(2) IMGUIのコンテンツ(3) IMGUIのコンテンツ(4) IMGUIのコンテンツ(5) IMGUIのコンテンツ(6) IMGUIのコンテンツ(7) IMGUIのコンテンツ(8) IMGUIのコンテンツ(9) 何となくボタン置いときます void IMGUIExecute() { for (int i = 0; i < 15; ++i) { EditorGUILayout.LabelField("IMGUIのコンテンツ(" + i + ")"); } }

62.

UMXLはこんな感じです UXML <ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements"> <ui:ScrollView style="position: absolute; left: 0px; right: 0; bottom: 50px; top: 0;"> <Style src="IMGUIContainerSample.uss" /> </ui:ScrollView> <ui:Button text="何となくボタン置いときます" style="position: absolute; right: 15px; bottom: 17px; height: 18px; width: 205px;"> <Style src="IMGUIContainerSample.uss" /> </ui:Button> </ui:UXML> UXMLの構造 rootVisualElement ScrollView name : ScrollList Button text:何となくボタン置いときます

63.
[beta]
こういう構造になって欲しいです
rootVisualElement
ScrollView
name : ScrollList
IMGUIContainer
onGUIHandler:IMGUIExecute
Button
text:何となくボタン置いときます
void IMGUIExecute()
{
for (int i = 0; i < 15; ++i)
{
EditorGUILayout.LabelField("IMGUIのコンテンツ(" + i + ")");
}
}
64.
[beta]
コードはこんな感じです
void OnEnable()
{
// uxml 読み込み
string path = "Assets/Editor/Samples/003/IMGUIContainerSample.uxml";
var asset =
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(path);
// 読み込んだUXMLをEditorWindowに配置
asset.CloneTree(this.rootVisualElement);
// IMGUIContainerをScrollViewの中にくいっつけます
var element = new IMGUIContainer(this.IMGUIExecute);
this.rootVisualElement.Query<ScrollView>().First().Add(element);
}
// OnGUIだと別で呼ばれてしまうので名前を変更…
void IMGUIExecute()
{
for (int i = 0; i < 15; ++i)
{
EditorGUILayout.LabelField("IMGUIのコンテンツ(" + i + ")");
}
}
65.

UIElementsで作っていくと... UXML <ui:Button name="SubmitBtn" text="決定" class="mybutton" data-origin="aaa"> <Style src="NewUI.uss" /> </ui:Button> Button name : ScrollList text : 決定 class : mybutton data-origin:aaa こういう感じでElementsに 独自データ埋め込めないです かね…?

66.

こういう書き方出来ないです... UXML <ui:Button name="SubmitBtn" text="決定" class="mybutton" data-origin="aaa"> <Style src="NewUI.uss" /> </ui:Button> Button name : Scroll text : 決定 class : utton dat gin:aaa

67.

ではどうしたらよいですか? - USS側に独自要素を置けるCustomStyleProperty - 独自のElementを作成

68.
[beta]
USS側に独自データ???
UXML
<ui:Label text="CustomPropValue"
class="mycustom">
<Style src="CustomPropertySample.uss" />
</ui:Label>
USS
.mycustom {
--data-origin:100;
}
ussのStyle側に独自
データ埋め込めます
69.
[beta]
CustomStylePropertyコード
// CustomStylePropertyを読むところ
rootVisualElement.Query<Label>(null, "mycustom").ForEach((label) =>
{
// CustomStyleResolvedEventに処理する
// このイベントが飛ぶ前だとCustomStylePropertyが読めない
label.RegisterCallback<CustomStyleResolvedEvent>(
(evt) =>
{
// CustomStylePropertyを読むところ
int val;
var customProperty = new CustomStyleProperty<int>("--data-origin");
bool res = label.customStyle.TryGetValue(customProperty, out val);
if (res)
{
label.text = "--data-origin:" + val;
}
else
{
label.text = "not found customProperty";
}
});
});
70.

実行します CustomPropertySample --data-origin:100

71.

色々と回り道過ぎます!!

72.

ならば、独自Elementの路線! UI Builder Explorer ▼ StyleSheet Add new selector... State Class ▼ Hierarchy Library ▼ Unity VisualElement Button Scroller Toggle Label Text Field Object Field Foldout ▶ Numeric Fields ▶ Value Fields ▶ Choice Fields ▶ Containers ▶ Inspectors 用意されたButtonとかLabel とかも良いのですが… 自分でも作成したいです (そしてタグに独自データも 差し込みたい)

73.
[beta]
VisualElementを継承したClassを用意!
public class MyAssetPreview : VisualElement
{
// 独自のデータAssetPath
string assetPath;
// UXMLからオブジェクトを生成するためのファクトリー
public new class UxmlFactory : UxmlFactory<MyAssetPreview, UxmlTraits> { }
// UXMLから要素を抜き出してきてオブジェクトに適用する部分
public new class UxmlTraits : VisualElement.UxmlTraits
{
//ここで独自のAttributeを指定します
UxmlStringAttributeDescription assetPathAttr =
new UxmlStringAttributeDescription { name = "assetPath" };
// 子要素を持つかどうか
public override IEnumerable<UxmlChildElementDescription> uxmlChildElementsDescription...
// パース時に呼び出される
public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)...
74.

実行します! CustomElementSample

75.

UIBuilderに出現!! UI Builder Explorer ▼ StyleSheet .fullrect Add new selector... State Class ▼ Hierarchy MyAssetPreview .fullrect Library ▶ Foldout ▶ Numeric Fields ▶ Value Fields ▶ Choice Fields ▶ Containers ▶ Inspectors ▶ Project ▼ UTJ MyAssetPreview Inspector ▼ MyAssetPreview Name View Data Key Picking Mode Position Tooltip Usage Hints None Tabindex 0 AssetPath Assets/Editor/Samples/005 ▼ Style Class List Add Style Class to List Extract Inlined Styles to New Class .fullrect x ▼ Matching Selectors ▶ .fullrect ▼ Inlined Styles ▼ Display Opacity 100 Display Visibility Overflow ▼ Position Position Absolute 用意した独自の UTJ.MyAssetPreviewが 出来ました!

76.

UIBuilderに出現!! UI Builder Explorer ▼ StyleSheet .fullrect Add new selector... State Class ▼ Hierarchy MyAssetPreview .fullrect Library ▶ Foldout ▶ Numeric Fields ▶ Value Fields ▶ Choice Fields ▶ Containers ▶ Inspectors ▶ Project ▼ UTJ MyAssetPreview Inspector ▼ MyAssetPreview Name View Data Key Picking Mode Position Tooltip Usage Hints None Tabindex 0 AssetPath Assets/Editor/Samples/005 ▼ Style Class List Add Style Class to List Extract Inlined Styles to New Class .fullrect x ▼ Matching Selectors ▶ .fullrect ▼ Inlined Styles ▼ Display Opacity 100 Display Visibility Overflow ▼ Position Position Absolute 独自要素AssetPathも! ※0.8.4-previewでは読み込んでも ココが空欄になってしまいます

77.

独自Elementは一度作ってしまえば、 色々なところで使えます

78.

本日のまとめ - 新しいやり方でEditor拡張作成のUIElements - UXMLが構造、USSがスタイル、C#がロジック - UXML/USSをオーサリングするツール「UI Builder」 (現時点での出来はお世辞にもよくないですが、ないよりはマシ - IMGUIとUIElementsはIMGUIContainerで共存できる - 独自のElementを作ることも出来る - UIElements Debugger便利

79.

その他情報 - 2019.3ベータ、現在UIElemelemtsではIME入力不可 (2020.1では修正済み、折を見てバックポートリクエストします) - 2019.3リリース前後でUIElementsがRuntime上で動くた めのPackageがpreviewとして出る予定 今はAnimationなどの機能がほとんどなし 今の所、2020.3頃完成予定 - XML内での改行方法は「&#xA;」