Unity：UI Elements で共通コントロールを作成する


# はじめに

*こんにちは、のんびりエンジニアのたっつーです。*
*[ブログ](https://www.fast-system.jp/)を運営しているのでよろしければ見てください。*

今回は「[いくつかサンプル書いたよ](https://www.fast-system.jp/unity-ui-elements-samples/)」の記事の１つの共通コントロールの作成方法と使い方を解説したいと思います。
ぜひ、お付き合いいただければと思います(#^^#)

# UI Elementsとは？

[こちらの記事](https://www.fast-system.jp/unity-uielements-howto/)をご参照いただければと思います。

# 共通コントロールの実装

さっそくですが、共通コントロールの実装をしてみたいと思います。

## 共通コントロール

***Editor フォルダ***を作成し、右クリックのメニューから***「UIElements Editor Window」***を選択してください。
![image-38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227880/f9a83c63-59c2-b94f-f0b9-4b7d15d73ac7.png)

「RedButton」を入力してください。
　※つけたいコントロールの名前に変更してしまって、OKです。
![image-39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227880/d4639eb9-7d28-4e07-0828-14631959aa30.png)

以下の３ファイルが生成されました、次にこの３ファイルを編集したいとおもいます。
![image-40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227880/3e29e79f-d3a6-cc41-fa39-8388d1787e15.png)

まずは、***RedButton.uxml は使わないので削除***してください！
※使っても実装できますが今回はスクリプトですべて生成していきます。

次に、RedButton.uss を以下のように変更してください。

```css:RedButton.uss
Button {
    font-size: 15px;
    -unity-font-style: bold;
    color: rgb(255, 0, 0);
}
```

次に、***RedButton.cs を以下のように変更***してください。

また、独自イベントとして clicked を定義しており、ボタンを押下イベントに合わせて clicked が呼ばれるようにしております。
また、***MyControls のネームスペースに変更する***のをお忘れずに！ 

```C#:RedButton.cs
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine.UIElements;


namespace MyControls
{
    internal class RedButton : VisualElement
    {
        public new class UxmlFactory : UxmlFactory<RedButton, UxmlTraits> { }

        public new class UxmlTraits : VisualElement.UxmlTraits
        {
            UxmlStringAttributeDescription m_Text =
                new UxmlStringAttributeDescription { name = "text", defaultValue = "none label" };

            public override IEnumerable<UxmlChildElementDescription> uxmlChildElementsDescription
            {
                get { yield break; }
            }

            public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
            {
                base.Init(ve, bag, cc);
                var text = m_Text.GetValueFromBag(bag, cc);
                ((RedButton)ve).Init(text);
            }
        }

        private Button m_button;

        public string text
        {
            get { return m_button.text; }
            set { m_button.text = value; }
        }

        public event Action<EventBase> clicked;

        public RedButton()
        {
            var styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/Editor/RedButton.uss");
            styleSheets.Add(styleSheet);

            m_button = new Button();
            m_button.text = "noen label";
            m_button.clickable.clickedWithEventInfo += Button_Clicked;
            hierarchy.Add(m_button);
        }

        public RedButton(string text) : this()
        {
            Init(text);
        }

        public void Init(string text)
        {
            this.text = text;
        }

        private void Button_Clicked(EventBase eb)
        {
            eb.target = this;
            clicked?.Invoke(eb);
        }
    }
}
```

## 呼び出し側

次に呼び出し側の実装になります。
先ほどと同じよう入力ですが今回は***「Sample1」で追加***してください。

![image-41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227880/a76a0f6d-a819-e611-61d7-fc07c058c4c2.png)

同じように３ファイルとも以下のように編集してください。

共通コントロールとして、以下の赤い文字のボタン（RedButton）を用意しました、これを使いまわせるパーツとして、ボタン１～３の計３つを作成しています。

ポイントとしては、***「xmlns:my=”MyControls”」 で my:RedButton が使える***ようになります。

```html:Sample1.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"
    xmlns:my="MyControls"
    xsi:noNamespaceSchemaLocation="../../UIElementsSchema/UIElements.xsd"
    xsi:schemaLocation="UnityEngine.UIElements ../../UIElementsSchema/UnityEngine.UIElements.xsd">

  <engine:Label text="Sample1 UXML" />

  <my:RedButton text="button1" name="btn1" />
  <my:RedButton text="button2" name="btn2" />
  <my:RedButton text="button3" name="btn3" />

  <engine:Label text="Sample1 UXML end" />
</engine:UXML>
```

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

ポイントとしては、各ボタンイベント（Clicked）にイベントを紐づけるために***「root.Q(“btn1”).clicked += ButtonClicked;」***で実行しています。

```c#:Sample1.cs
using MyControls;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

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

    public void OnEnable()
    {
        VisualElement root = rootVisualElement;

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

        root.Q<RedButton>("btn1").clicked += ButtonClicked;
        root.Q<RedButton>("btn2").clicked += ButtonClicked;
        root.Q<RedButton>("btn3").clicked += ButtonClicked;
    }

    void ButtonClicked(EventBase eventBase)
    {
        var redButton = (RedButton)eventBase.target;
        Debug.Log($"Clicked {redButton.text} RedButton!! ");
    }
}
```

## 動作結果

どうでしたか簡単に実装が行えたと思います。
今回は、RedButton.uxml は使わない実装でしたが使って実装する場合もそんなに難しくないと思いますのでぜひチャレンジしてみてください！

![sample1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227880/10e6965a-5d70-925d-28f4-46b2ded529cf.gif)


# 終わりに

*よければ ブログ「[Unity+AssetStoreおすすめ情報](https://www.fast-system.jp/)」の方にも色々記載しているのでぜひご参照いただければと思います。*