アマゾンバナーリンク

ディスプレイ広告

スポンサーリンク

【Unity】UIElementsの使い方まとめ

こんにちは!ジェイです。UnityのUIを実装する時にはいくつか種類があって、みなさんも一度は使った事があると思います。今日はその中でUIElementsについて解説していきます。

記事内広告

UIElementsとは?

UnityでUIを組み立てる方法は3つあります。

  • IMGUI
  • uGUI
  • UIElement

上記の3つの方法でUIを作れます。

IMGUI

最も古い方法で以下の様に使います。

private void OnGUI()
{
    if (GUILayout.Button("Button"))
    {
        Debug.Log("Buttonが押されました");
    }
}

私は今でもよく使いますが、通常はあまり使われなくなりました。エディタ拡張ではこれを使ってUIを組み立てる事が多いです。

uGUI

UnityエディタのヒエラルキーのメニューのUIからImageやButtonやLabelなど、様々なUIが揃っているので、直感的に使う事ができます。

UIElements

Unity2019.1からUIElementsが登場しました。これによって、CSSやXMLに似た構文でUIを作れるようになりました。見た目の装飾にはUSSと呼ばれるスタイルシートを使って、階層構造の定義にはUXML形式を使います。

UIElements

この様にファイルがそれぞれ分離して役割を果たすようになったので、シンプルになりました。もちろんUIElementはエディタ拡張にも使えるので、かなりのメリットになるでしょう。ただIMGUIもすぐには廃止にはならないようなので、急いで覚えなければならないということではありません。

準備

エディタ拡張を使うために、階層はどこでもよいのですが、Editorフォルダ内スクリプトを置く必要があります。
またUXML、USSファイルについてはAssets/Editor/Resources以下に置くことにしましょう。
Assetsフォルダ内にEditorフォルダを作成し、Editorフォルダ内にScriptsフォルダとResourcesフォルダを作成して下さい

Assets
└─ Editor
     ├── Resources
     └── Scripts

EditorWindowを作る

さっそくウィンドウを作ってみます。Editor/Scriptsフォルダ内に以下のスクリプトを作成して下さい。

using UnityEditor;
using UnityEngine.UIElements; // UIElementsを使うのでusingする

public class Sample : EditorWindow
{
    [MenuItem("Sample/Open")]
    public static void ShowWindow()
    {
        GetWindow<Sample>("Title");
    }

    // 有効になった時に実行される
    private void OnEnable()
    {
        // ラベルのVisualElement
        var label = new Label("test");

        // EditorWindowのrootの子としてlabelを追加
        rootVisualElement.Add(label);
    }
}

実行結果

rootVisualElementとは

UIElementsでは、全てのUI(ラベル、ボタン、トグル、スライダー、ボックスなど)はVisualElementというサブクラスから継承されてます。そして、VisualElementは他のVisualElementを子要素にできて、VisualElement同士で親子関係を木構造で形成します。

このVisualTreeの「根」となっているVisualElementがrootVisualElementです。EditorWindowにUIを表示したい場合はこのrootVisualelementに子要素として追加する必要があります。今回はrootVisualElementにLabelを子要素として追加してます。

UXMLファイルを作成する

先程はC#スクリプトでラベルを作成しましたが、UXMLでも行えます。

Editor/Resouces内でCreate>UI Toolkit>UI Documentで"Sample"という名前のUXMLファイルを作成しましょう。

生成されたコードの中に

<engine:Label text="test"/>

上の1行を挿入して

<?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="test"/>
</engine:UXML>

C#スクリプトを以下のように書き換えます。

using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

public class Sample: EditorWindow
{
    [MenuItem("Sample/Open")]
    public static void ShowWindow()
    {
        GetWindow<Sample>("Title");
    }

    private void OnEnable()
    {
        // UXMLファイルを読み込む
        var visualTree = Resources.Load<VisualTreeAsset>("Sample");

        // UXMLで定義したVisualTreeを生成し、そのrootとしてrootVisualElementを設定
        visualTree.CloneTree(rootVisualElement);
    }
}

先程と同じ様にメニューバーから、Sample>OpenをクリックするとtestというLabelが1つだけのウィンドウが表示されます。

複数のLabelを作る

<?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="hogehoge"/>
    <engine:Label text="あいうえお"/>
    <engine:Label text="ABCDE"/>
    <engine:Label text="12345"/>
    <engine:Label text="テストテスト"/>
</engine:UXML>

Boxを作る

今作った複数のLabelをBoxで囲みます。

<?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"
>
    <!-- Boxを作成 -->
    <engine:Box >
        <engine:Label text="hogehoge"/>
        <engine:Label text="あいうえお"/>
        <engine:Label text="ABCDE"/>
        <engine:Label text="12345"/>
        <engine:Label text="テストテスト"/>        
    </engine:Box>
</engine:UXML>

先程のUXMLをこの様に変更し、5つのLabelをBoxの子に設定すると、これらのLabelを囲むBoxが作られます。

Buttonを作る

UXMLを以下の様に変更します。

<?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 text="OK"/>
</engine:UXML>

以下の様なButtonが作られます。

Buttonのクリック時の処理を登録する

更にUXMLを変更しButtonをクリックした時の処理を追加します。

<?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 text="OK" name="OKButton"/> <!-- nameを設定する -->
</engine:UXML>

以下のようにOKボタンが追加されます。

次にC#スクリプトを以下の様に変更します。

using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

public class Sample : EditorWindow
{
    [MenuItem("Sample/Open")]
    public static void ShowWindow()
    {
        GetWindow<Sample>("Title");
    }

    private void OnEnable()
    {
        var visualTree = Resources.Load<VisualTreeAsset>("Sample");
        visualTree.CloneTree(rootVisualElement);

        // 型と名前を指定してButtonを取得
        var button = rootVisualElement.Q<Button>("OKButton");

        if (button != null)
        {
            // Buttonを押した時の処理を登録
            button.clickable.clicked += () => Debug.Log("OK");
        }
    }
}

これでOKを押した時に以下のようなログがでます。

Image from Gyazo

Buttonのサイズを変更する

まずはUXMLを変更して、先程作成した"OKButton"に対して新たにclassを定義します。

<?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 text="OK" name="OKButton" class="OKButton"/> <!-- classを定義する -->
</engine:UXML>

次にEditor/Resources内でCreate>UI Toolkit>Style Sheetにより"SampleStyle.uss"を作成して下さい。

次にテンプレートのコードを削除して以下の様に変更します。

.OKButton {
    width: 50px;
    height: 50px;
}

C#スクリプトにも、USSファイルを読み込んで設定する処理を追加します。

using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

public class Sample : EditorWindow
{
    [MenuItem("Sample/Open")]
    public static void ShowWindow()
    {
        GetWindow<Sample>("Title");
    }

    private void OnEnable()
    {
        var visualTree = Resources.Load<VisualTreeAsset>("Sample");
        visualTree.CloneTree(rootVisualElement);

        // USSファイルを読み込む
        var styleSheet = Resources.Load<StyleSheet>("SampleStyle");
        // USSファイルをVisualTreeに設定
        rootVisualElement.styleSheets.Add(styleSheet);

        var button = rootVisualElement.Q<Button>("OKButton");

        if (button != null)
        {
            button.clickable.clicked += () => Debug.Log("OK");
        }
    }
}

Buttonのサイズがちゃんと変わってますね。USSで設定したように、縦50px、横50pxになっています。

USSの説明

SampleStyle.ussの意味はOKButtonと名前のクラスが付いているUIの横と縦の長さを50pxにするという意味です。

.OKButtonsと「.」から始まる場合は、その後に続けて書くのは、UXMLで定義したクラス名でなければなりません。

更に、#から始まる場合はUXMLで定義したVisualElementのnameを続けて書く必要があります。

今回の場合はVisualElementのnameも"OKButton"だったので以下のように定義します。

#OKButton {
    width: 50px;
    height: 50px;
}

また、.も#もつけない場合は、UIのC#でのクラス名を書かなければなりません。今回の場合はButtonなので、

Button {
    width: 50px;
    height: 50px;
}

という感じになります。他にもButtonが存在するとすべて同じスタイルが適用されてしまうので、注意しましょう。

他にも文字の色を変えたり色々な事ができるみたいです。

まとめ

UI Elementsを使うことによって、UXMLでUIの構造を、USSでUIのデザインを、定義することができます。HTMLやCSSのようにそれぞれの役割を分離できるので、保守性などもよくなるので、ぜひ覚えておきたい技術です。

アイコンは鬼塚@取締役さんからお借りしました。

+3