9
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

サムザップ #2Advent Calendar 2019

Day 23

【Unity2019】UIElementsを触ってみた

Last updated at Posted at 2019-12-23

本記事は、サムザップ Advent Calendar 2019 #2 の12/23の記事です。

Unity2019.1新機能「UIElements」とは

IMGUIからUIElementsに置き換わるであろう新しいGUIシステムです。

UIElementsはUXML/USS/UQueryで構成されます。

所感、
UXMLはUnityで扱えるXMLベースのマークアップ言語のイメージ
USSはCSSに近い書き方でデザインが出来そう
UQueryはC#で主にロジック部分を書くのかな…
って感じでした。

Unity Blog「Unity 2019.1 の UIElements の新機能」

テキスト読んだだけじゃ頭に入ってこないので、取り敢えず触ってみよう。。

「UIElements」を触ってみる

開発環境

今回使用する環境はUnity2019.3.0f1です。
(普段Unity2018のLTS版触っているから、Editorの見た目に違和感…w)

「UIElements」をHelloWorld

1.「UIElements」のEditorWindowを開く

右クリックでメニュー開く > [Create] > [UIElements] > [Editor Window]
もしくは
[Assets] > [Create] > [UIElements] > [Editor Window]
CreateUIElements.png

2.「UIElements」EditorWindowで作成

CreateUIElementsWindow.png ### 3.準備完了 HelloWorldWindow.png ## 作成されたデフォルトの中身 ### DefaultUIElementsEditorWindow.cs ``` using UnityEditor; using UnityEngine; using UnityEngine.UIElements; using UnityEditor.UIElements;

public class DefaultUIElementsEditorWindow : EditorWindow
{
[MenuItem("Window/UIElements/DefaultUIElementsEditorWindow")]
public static void ShowExample()
{
DefaultUIElementsEditorWindow wnd = GetWindow();
wnd.titleContent = new GUIContent("DefaultUIElementsEditorWindow");
}

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

    // VisualElements objects can contain other VisualElement following a tree hierarchy.
    VisualElement label = new Label("Hello World! From C#");
    root.Add(label);

    // Import UXML
    var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/Editor/DefaultUIElementsEditorWindow.uxml");
    VisualElement labelFromUXML = visualTree.CloneTree();
    root.Add(labelFromUXML);

    // A stylesheet can be added to a VisualElement.
    // The style will be applied to the VisualElement and all of its children.
    var styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/Editor/DefaultUIElementsEditorWindow.uss");
    VisualElement labelWithStyle = new Label("Hello World! With Style");
    labelWithStyle.styleSheets.Add(styleSheet);
    root.Add(labelWithStyle);
}

}

### DefaultUIElementsEditorWindow.uxml

<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="Hello World! From UXML" />

### DefaultUIElementsEditorWindow.uss

Label {
font-size: 20px;
-unity-font-style: bold;
color: rgb(68, 138, 255);
}


# :warning: **つまづきポイント** :warning:
## 「UIElements」のEditorWindowでエラー
下記のようなエラーが発生した(何起因で発生したかは分からない…) 
<img width="250" alt="CreateUIElementsWindowError.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209889/651b95c1-dea2-220c-3a63-c554e8be6550.png">
:hospital: **対処法** :hospital:
Editorフォルダ配下で
右クリックでメニュー開く > [Update UIElements Schema]
もしくは
[Assets] > [Update UIElements Schema]
<img width="240" alt="Error.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209889/1bef12fa-bbfe-5e08-52ae-7ef25078277c.png">
## UXMLのタグ頭に「engine:」を付けないとエラー
Unity公式のリファレンスでは「engine:」がタグ頭に付いてる例と付いていない例がある。毎回「engine:」をタグ頭につけるような冗長な書き方は避けたいけど、「engine:」を失くすとエラーになる。。
:hospital: **対処法** :hospital:
デフォルトで生成されるUXMLは下記のようにnamespaceが指定されているので、毎回「engine:」をタグ頭につける必要があったっぽい
```xmlns:engine="UnityEngine.UIElements"```
下記のようにnamespaceを失くすことで、毎回「engine:」をタグ頭に付けなくても良くなる。
※必要な時にnamespaceで区切ろうかな…
```xmlns="UnityEngine.UIElements"```
# 軽く基盤を作ってみる
## built-inのHelloWorldファイルは色々イケてない
1. UXML内に既にnamespaceが指定されている
2. USSがLabelにしか適用されない

ってことで、
とりあえず、複数のUXMLとUSSをデータとして受け取ったら、それを使ってEditorWindowを構築してくれるものが欲しい

## 試しに作ってみる
コレで、基盤のEditorWindowクラスが設定データを元に構築してくれる。
### 実際の基盤クラス
EditorWindowのデータクラス

using UnityEngine;
using UnityEngine.UIElements;

namespace System.Common.Editor.Window
{
///


/// エディタウィンドウのデータの設定クラス
///

[CreateAssetMenu(fileName = "EditorWindowSettingData", menuName = "Tools/Editor Window/Create Setting Data/EditorWindowData")]
public class EditorWindowData : ScriptableObject
{
///
/// UXML一覧
///

[SerializeField]
private VisualTreeAsset[] uxmlArray;
public VisualTreeAsset[] UxmlArray { get => uxmlArray; }

    /// <summary>
    /// USS一覧
    /// </summary>
    [SerializeField]
    private StyleSheet[] ussArray;
    public StyleSheet[] UssArray { get => ussArray; }
}

}


EditorWindowのBaseクラス

using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

namespace System.Common.Editor.Window
{
///


/// エディタウィンドウのベースクラス
///

public class EditorWindowBase : EditorWindow where T : EditorWindow
{
///
/// 設定データ
///

[SerializeField]
private EditorWindowData editorSettingData;

    private static T _instance;
    public static T Instance { get => _instance; private set => _instance = value; }

    /// <summary>
    /// Window開く
    /// </summary>
    public static void Open()
    {
        Instance = GetWindow<T>();
        Instance.titleContent = new GUIContent(Instance.GetType().Name);
    }

    /// <summary>
    /// アクティブ処理
    /// </summary>
    protected virtual void OnEnable() { Init(); }

    /// <summary>
    /// 非アクティブ処理
    /// </summary>
    protected virtual void OnDisable() { Disable(); }

    /// <summary>
    /// 初期化
    /// </summary>
    protected virtual void Init()
    {
        if (editorSettingData == null)
            return;

        // Each editor window contains a root VisualElement object
        VisualElement root = rootVisualElement;
        if (root == null)
            return;

        // Import UXML
        foreach (var uxml in editorSettingData.UxmlArray)
        {
            if (uxml == null)
                continue;

            root.Add(uxml.CloneTree());
        }

        // Import USS
        foreach (var uss in editorSettingData.UssArray)
        {
            if (uss == null)
                continue;
            root.styleSheets.Add(uss);
        }
    }

    /// <summary>
    /// 停止処理
    /// </summary>
    protected virtual void Disable() { }
}

}


### テストデータ
EditorWindowの呼び出し口(TestEditorWindowを開くだけ)

using UnityEditor;
using Test.Editor.Window;

namespace Tools
{
public class ToolMenu
{
[MenuItem("Tools/Editor Window/TestWindow")]
public static void Open() { TestEditorWindow.Open(); }
}
}


EditorWindow(中身なし)

using System.Common.Editor.Window;

namespace Test.Editor.Window
{
public class TestEditorWindow : EditorWindowBase
{

}

}


UXML(中身なし)

<UXML
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="UnityEngine.UIElements"
xsi:noNamespaceSchemaLocation="../../UIElementsSchema/UIElements.xsd"

``` # 参考にしたリファレンス UIElements Developer Guide https://docs.unity3d.com/Manual/UIElements.html UXML関連 https://docs.unity3d.com/Manual/UIE-ElementRef.html

触ってみて…

IMGUIよりUIElementsの方が、Viewと処理を分けられるから読みやすそう。(小規模なツールはIMGUIの方が書きやすそうだけど…)

でも、毎回タグ頭に「engine:」をつけるのはダルいので、UXML内の対応で回避してみた。
UXMLのテンプレートを用意したり、SerializeField表示のためにBindして表示できるようにしたりと、基盤としてやらなきゃいけないことは結構あるけど、、、
って、今見たらVisualElementBindがなさそう…
visualElement.Bind(serializedData);的な感じで行けると思ったんだけど…
SerializeField表示方法をまた調べなきゃ…笑

Assetsと同階層のUIElementsSchemaディレクトリにUIElements.xsdとか色々定義されていたので、CustomしたXMLSchemaを作ってみるのをどっかで試してみたい。

明日は @m-nakayama さんの記事です。

9
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?