はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【Unity】UI Toolkitの公式ドキュメントのGetting Startedをやったメモ

はじめに

今回は新しいUIシステムであるUI Toolkitについて取り上げたいと思います。

今回作るUI

この記事は以下のUI Toolkitの導入記事の自分向けの備忘録になります。見づらいですし、公式ドキュメントを見た方が正確な情報が書いてありますのであしからず。
docs.unity3d.com

また一部公式ドキュメントと異なるコードを書いていますので注意してください。

概要

UI Toolkit(旧: UI Elements)はuGUIIMGUIのような既存にUIシステムに対して標準的なWeb技術に触発された新しいUIシステムです。

UI Toolkit is a collection of features, resources, and tools for developing user interface (UI). You can use UI Toolkit to develop custom UI and extensions for the Unity Editor, runtime debugging tools, and runtime UI for games and applications.

// DeepL翻訳
UI Toolkitは、ユーザーインターフェース(UI)を開発するための機能、リソース、ツールのコレクションです。UI Toolkitを使用して、Unityエディタ用のカスタムUIや拡張機能、ランタイムデバッグツール、ゲームやアプリケーションのランタイムUIを開発できます。

UI Toolkit - Unity マニュアル

UI ToolkitRuntimeでもEditorでも動作しますが、公式ではUnity2022で以下のように記載されています。新しいので全部UI Toolkitを使えば良いのかと言うと、それぞれのケースに置いて得手不得手があるので詳細は公式ドキュメントを参照してみてください。

2022 推奨 代替案
Runtime Unity UI(uGUI) UI Toolkit
Editor UI Toolkit IMGUI

Unity の UI システムの比較 - Unity マニュアル

Getting Started

まずはUI ToolkitEditor UIEditor Windowを作成してみましょう。具体的には以下の操作を行います。

  1. カスタムエディターウィンドウの作成
  2. UI Builderを使用してUIコントロールを加える
  3. UXMLを使用してUIコントロールを加える
  4. C#スクリプトを使用してUIコントロールを加える

カスタムエディターウィンドウの作成

Project Window上で右クリックし、Create > UI Toolkit > Editor Windowを選択します。

Create > UI Toolkit > EditorWindow

するとUI Toolkit Editor Window Creatorが立ち上がるはずです。これを用いることで.cs.uxml.ussを一括で作成することができます。

UI Toolkit Editor Window Creator

今回はMyCustomEditorと入力をし、USSのチェックボックスを外して.cs.uxmlのみ作成します。Confirmボタンを押せばファイルが生成されます。

.csと.uxmlのみ作成

UI Builderを使用してUIコントロールを加える

UI コントロールをウィンドウに加える方法は複数ありますが、実際に利用する場面では意外とUXMLを自分で書いたりはそこまでしません。基本的にUI Builderという視覚的に.uxml.ussを作成・編集できるツールを利用することが多いです。

UI Builder では、UI Toolkit で使用する UI ドキュメント (.uxml) やスタイルシート (.uss) などの UI アセットを視覚的に作成、編集できます。

UI Builder - Unity マニュアル

UI Builderを開く

.uxmlファイルをダブルクリックするとUI Builderが開きます

UI Builder
ButtonとToggleを配置する

左下のLibraryの中からControls > ButtonControls > ToggleHierarchyにドラッグ&ドロップすることで配置することができます。

Controls > ButtonとControls > Toggleを配置
インスペクター操作

Hierarchy上でLableを選択するとInspectorが表示されます。その中のTextフィールドにThese controls were created in UI Builderと入力すると、ViewPort上の表示が変わっていることが確認できます。

InspectorによるTextフィールドの編集

同様にして以下の操作を行なってください。

  • Hierarchy上でButtonを選択して、TextフィールドにThis is Button 1と入力、Nameフィールドにbutton1と入力。
  • Hierarchy上でToggleを選択して、LabelフィールドにNumber?と入力、Nameフィールドにtoggle1と入力。
Nameフィールド, Textフィールド, Labelフィールドを入力
変更をセーブする

以下のどちらかの操作でセーブします。

  • UI Builder の Viewport ペインツールバーの File > Save メニューを使用します。
  • Ctrl/CMD + S を使います。また、エディターでシーンを保存するように指示されます。

UI Builder のインターフェース概要 - Unity マニュアル

正しくセーブされると名前の横のアスタリスク(*)が消えるはずです。

完成形を確認する

メニューバーのWindow > UI Toolkit > MyCustomEditorを選択し、作成したEditorWindowの確認をしてみましょう。

Window > UI Toolkit > MyCustomEditor
完成系

UXMLを使用してUIコントロールを加える

基本的にUI Builderを利用することが多いですが、UXMLを自身で弄ることも当然できます。その手法を紹介したいと思います。

UI Documentを作成する

ProjectビューにてAssets > Create > UI Toolkit > UI Documentをクリックして、MyCustomEditor_UXML.uxmlファイルを生成し中身を書き換えます。

Assets > Create > UI Toolkit > UI Document
<?xml version="1.0" encoding="utf-8"?>
<engine:UXML
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:engine="UnityEngine.UIElements"
    xmlns:editor="UnityEditor.UIElements"
    xsi:noNamespaceSchemaLocation="../../UIElementsSchema/UIElements.xsd"
>
    <engine:Label text="These controls were created with UXML." />
    <engine:Button text="This is button2" name="button2"/>
    <engine:Toggle label="Number?" name="toggle2"/>
</engine:UXML>

作成した.uxmlを実際に描画させるためにMyCustomEditor.csに以下のコードを追加します。

// MyCustomEditor_UXML.uxmlへの参照をここで入力する
[SerializeField] private VisualTreeAsset _uxmlTree;
// root(VisualElements)の子としてMyCustomEditor_UXML.uxmlを追加する
root.Add(_uxmlTree.Instantiate());
public class MyCustomEditor : EditorWindow
{
    // .uxmlをインスペクターより設定する
    [SerializeField] private VisualTreeAsset _uxmlTree;
    
    [MenuItem("Window/UI Toolkit/MyCustomEditor")]
    public static void ShowExample()
    {
        MyCustomEditor wnd = GetWindow<MyCustomEditor>();
        wnd.titleContent = new GUIContent("MyCustomEditor");
    }

    public void CreateGUI()
    {
        // Each editor window contains a root VisualElement object
        VisualElement root = rootVisualElement;

        // rootVisualElementの子に_uxmlTreeを追加する
        root.Add(_uxmlTree.Instantiate());
    }
}

この[SerializeField]したものはProjectビューよりMyCustomEditor.csを選択し、Inspectorにて.uxmlを設定してあげれば完成です。

Inspectorに.uxmlを設定する
実際のウィンドウ

C#スクリプトを使用してUIコントロールを加える

MyCustomEditor.csを以下のように変更します。

public class MyCustomEditor : EditorWindow
{
    [MenuItem("Window/UI Toolkit/MyCustomEditor")]
    public static void ShowExample()
    {
        MyCustomEditor wnd = GetWindow<MyCustomEditor>();
        wnd.titleContent = new GUIContent("MyCustomEditor");
    }

    public void CreateGUI()
    {
        // Each editor window contains a root VisualElement object
        VisualElement root = rootVisualElement;

        // rootVisualElementの子にLabelを追加する
        Label label = new Label("These controls were created using C# code.");
        root.Add(label);
        
        // rootVisualElementの子にButtonを追加する
        Button button = new Button();
        button.name = "button3";
        button.text = "This is button3.";
        root.Add(button);

        // rootVisualElementの子にToggleを追加する
        Toggle toggle = new Toggle();
        toggle.name = "toggle3";
        toggle.label = "Number?";
        root.Add(toggle);

        // Import UXML
        var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/UIToolkit/MyCustomEditor.uxml");
        VisualElement labelFromUXML = visualTree.Instantiate();
        root.Add(labelFromUXML);
    }
}
完成図

UIコントロールの動作を定義する

  • ボタンをクリックすると、コンソールにメッセージが表示される
  • トグルを選択すると、ボタンが何回クリックされたかがコンソールに表示される
public class MyCustomEditor : EditorWindow
{
    [SerializeField] private VisualTreeAsset _uxmlTree;

    private int _clickCount = 0;

    private const string ButtonPrefix = "button";
    
    [MenuItem("Window/UI Toolkit/MyCustomEditor")]
    public static void ShowExample()
    {
        MyCustomEditor wnd = GetWindow<MyCustomEditor>();
        wnd.titleContent = new GUIContent("MyCustomEditor");
    }

    public void CreateGUI()
    {
        // Each editor window contains a root VisualElement object
        VisualElement root = rootVisualElement;

        // rootVisualElementの子にLabelを追加する
        Label label = new Label("These controls were created using C# code.");
        root.Add(label);
        
        // rootVisualElementの子にButtonを追加する
        Button button = new Button();
        button.name = "button3";
        button.text = "This is button3.";
        root.Add(button);

        // rootVisualElementの子にToggleを追加する
        Toggle toggle = new Toggle();
        toggle.name = "toggle3";
        toggle.label = "Number?";
        root.Add(toggle);

        // Import UXML
        var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/UIToolkit/MyCustomEditor.uxml");
        VisualElement labelFromUXML = visualTree.Instantiate();
        root.Add(labelFromUXML);
        
        SetupButtonHandler();
    }

    private void SetupButtonHandler()
    {
        VisualElement root = rootVisualElement;
        
        // Buttonを全て取得する
        UQueryBuilder<Button> buttons = root.Query<Button>();
        buttons.ForEach(RegisterHandler);
    }

    private void RegisterHandler(Button button)
    {
        button.RegisterCallback<ClickEvent>(PrintClickMessage);
    }
    
    private void PrintClickMessage(ClickEvent evt)
    {
        VisualElement root = rootVisualElement;

        ++_clickCount;

        //Because of the names we gave the buttons and toggles, we can use the
        //button name to find the toggle name.
        Button button = evt.currentTarget as Button;
        string buttonNumber = button.name.Substring(ButtonPrefix.Length);
        string toggleName = "toggle" + buttonNumber;
        
        // toggleNameに一致するToggleを一つ取得する
        Toggle toggle = root.Q<Toggle>(toggleName);

        Debug.Log("Button was clicked!" +
                  (toggle.value ? " Count: " + _clickCount : ""));
    }
}