UIElementsでのエディタ拡張 自作ウィンドウとボタンを表示してみる

お久しぶりです。近藤です。

久しぶりの間に、Unityに新しいエディタ拡張の仕組みが追加されましたね!
今回はUnity2019の新機能「UIElements」を使用したエディタ拡張の紹介をしていこうと思います。
本当に初歩的な紹介になりますが、お付き合いよろしくお願いします!!

使用しているUnityのバージョンは2019.3.0f3です。

まずUIElementって???

公式ドキュメント
https://docs.unity3d.com/Manual/UIElements.html

Unity2019から追加されたUIの新しい作成方法です。
ButtonやLabelなど、従来のGUILayoutで使用していたような部品をツリー状に構築していくイメージでしょうか?

以前まではほぼC#スクリプトで完結していたエディタ拡張ですが、
この機能を使用するにあたり新しくUXML/USSというHTML/CSSのような記述もできるようになっています。


今後、今までのエディタ拡張(IMGUI)からこちらに置き換わっていくようです。Unity2020ではランタイム側も対応するみたいです。

ウィンドウを作成する

今回は簡単な実装として自作ウィンドウとコンソールにログを出力するボタンを作成します。

まずは従来の方法(IMGUI)

using UnityEditor;
using UnityEngine;

public class ButtonWindowIMGUI : EditorWindow
{
    // メニューに追加
    [MenuItem("Window/ButtonWindow")]
    public static void ShowWindow()
    {
        // ウィンドウの作成
        var wnd = GetWindow<ButtonWindowIMGUI>();
        // ウィンドウタイトルの設定
        wnd.titleContent = new GUIContent("ButtonWindow");
    }
    private void OnGUI()
    {
        // ボタン表示
        if (GUILayout.Button("ボタン"))
        {
            // ボタンが押された場合、コンソールにログを表示
            Debug.Log("ボタン押されたよ");
        }
    }
}

EditorWindowクラスを継承したウィンドウを作成してOnGUI内でボタンの作成を行っています。
このスクリプトをEditorフォルダに保存してWindow/ButtonWindowを選択するとウィンドウが表示されます。
The・サンプルって感じが好きです。

UIElementsを使用して同じものを実装する

using UnityEngine;
using UnityEditor;
using UnityEngine.UIElements;

public class ButtonWindowUIElement : EditorWindow
{
    // メニューに追加
    [MenuItem("Window/ButtonWindow/UIElements")]
    public static void ShowWindow()
    {
        // ウィンドウの作成
        var wnd = GetWindow<ButtonWindowUIElement>();
        // ウィンドウタイトルの設定
        wnd.titleContent = new GUIContent("ButtonWindow");
    }

    private void OnEnable()
    {
        // ボタン作成
        var root = CreateGUI();
        // ボタンをウィンドウに追加
        rootVisualElement.Add(root);
    }

    private VisualElement CreateGUI()
    {
        // ボタン作成
        var button = new Button
        {
            text = "ボタン"
        };

        // ボタンが押された場合、コンソールにログを表示
        button.clicked += () => {
            Debug.Log("ボタン押されたよ");
        };

        return button;
    }
}

ウィンドウ作成まで変わらないですが、ボタンの作成方法に違いがあり、
OnGUI内で作成するのではなく、VisualElementを作成し、それをウィンドウに追加するようになりました。

このVisualElementを使用するようになったのが、UIElementsでのエディタ拡張で一番の変更点だと思います。

今まではRepaintのためにOnGUI内にLayoutの記述を行ってきましたが、各々のVisualElementが必要に応じて再描画するようになります。なのでOnGUIでの実装ではなくなりました。

UXML/USSを使用する

最後にHTML/CSSっぽいUXML/USSを使用してウィンドウの作成とボタン表示を行います。

名前から何となく察しはつくと思いますが、
UXMLがXML記法
USSがCSS記法で記述します。

using UnityEngine;
using UnityEditor;
using UnityEngine.UIElements;
using UnityEditor.UIElements;

public class ButtonWindowUIElement : EditorWindow
{
    // メニューに追加
    [MenuItem("Window/ButtonWindow/UIElements")]
    public static void ShowWindow()
    {
        // ウィンドウの作成
        var wnd = GetWindow<ButtonWindowUIElement>();
        // ウィンドウタイトルの設定
        wnd.titleContent = new GUIContent("ButtonWindow");
    }

    private void OnEnable()
    {
        var root = CreateGUI();
        rootVisualElement.Add(root);
    }

    private VisualElement CreateGUI()
    {
        // UXMLからツリー構造を読み取り
        var tree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/Editor/ButtonWindow.uxml");

        // Button_0という名前のボタンが押された場合
        var root = tree.CloneTree();
        root.Query<Button>("Button_0").First().clicked += () => {
            Debug.Log("ボタン押されたよ");
        };

        // USSからスタイル読み取り
        var styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/Editor/ButtonWindow.uss");
        root.styleSheets.Add(styleSheet);

        return root;
    }
}
<?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:Button name ="Button_0" text="ボタン"/>
</engine:UXML>
Button {
    font-size: 20px; /*文字サイズ*/
    -unity-font-style: bold; /*文字スタイル ボールドに*/
    color: rgb(68, 138, 255); /*色 水色に*/
}

CretateGUI内で記述していたボタン作成がUXML上に移動し、そのUXMLのスタイルをUSSで変更するといった感じです。

ボタンを押したときの動作はスクリプトで記述していますが、配置や見た目などはスクリプトではなく、
UXML/USSになるので編集を行ってもコンパイルを待たず即反映されるがGOOD

先ほどまでのウィンドウと殆ど同じものが出来ますが、スタイルの変更を行っているのでボタンに表示されている文字色が水色になっています。

UIElements Debuggerについて

UIElementsで作成したウィンドウはUIElements Debuggerでツリー構造を確認することができ、編集も行うことができます。とても便利。

まとめ

見た目と機能が分かりやすく分離されたので、個人的には使いやすくなったと思います。
機能は一緒で見た目だけ変更というのがかなりやり易くなりました。

短いですが今回はここまでとさせていただきます。お付き合いいただきありがとうございました。