3
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

Organization

XMLとか知らねぇよ!!C#だけでUIElements式エディタ拡張をする入門~基礎編~

はじめに

こんにちは、アドベントカレンダー2日目担当の避雷です。お仕事でエディタ拡張する必要があり、たまたまバージョン的にもUIElementsが使える感じだったので試しに使ってみたところ結構便利だったので紹介します

  • 基礎編(ここ)
  • style編

UIElementsとは

Unityが公式でサポートしている新しいUIデザインの手法です。xamlやCSSと同様の記法によってUIのデザインができます。Unity的にはuGUIからこちらへの移行を試みているようで、uGUIの方はスタメンから外されてpackageManagerへの流刑を食らっています。主観としてはUnityはゲームエンジンからソフトウェア開発プラットフォームになろうとしていて、ゲーム以外のUI設計を可能にしたい、みたいなモチベなのかなと思いました。
https://blogs.unity3d.com/jp/2019/04/23/whats-new-with-uielements-in-2019-1/

メリット

  • OnGUIコールバックベースからの脱却
  • UIにeventを登録する感じでかけるので可読性が上がる。
  • コードが書きやすい。
  • xamlによってデザインするタイプの開発経験があると同じようなスタイルで触れる。

デメリット

  • 必要になる技術が多い
  • プロジェクトメンバー全員がxamlとかCSSとか触れるスキルセットじゃないと属人性がキツくなりがち
  • 普通にuGUIの代替にはならないと思う

実装

導入

右クリックから「UIElements Editor Window」を選択して元となるスクリプトを生成しましょう。Editorフォルダ無いじゃないと生成出来ないよって怒られるので注意。
image.png

生成されるウィンドウを見てみよう

デフォルトだとWindow/UIElementから先ほど作成したwindowを呼び出すことができます。
image.png
HelloWorldって生成されていますね。

生成されたコードを読んでみよう

先ほど生成したC#を覗いてみましょう。

Test.cs
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using UnityEditor.UIElements;


public class Test : EditorWindow
{
//メニューバーから呼び出すためのattribute
    [MenuItem("Window/UIElements/Test")]
    public static void ShowExample()
    {
        Test wnd = GetWindow<Test>();
//↓タイトルの設定
        wnd.titleContent = new GUIContent("Test");
    }

    public void OnEnable()
    {
//rootVisualElement(表示されるUIすべての親となるVisualElementの取得)
        VisualElement root = rootVisualElement;
//VisualElementの一つ、ラベルを生成
        VisualElement label = new Label("Hello World! From C#");
//rootの子要素として追加、デフォルトだと子要素は上から下にstackされていく
        root.Add(label);
    }
}

なるほど、C#でもrootを根とした樹状構造を構築してUIを表現することが出来るみたいです。早速色々作ってみることにしましょう。

ラベルをつける

これに関してはそのままLabelというクラスがあるのでこれをインスタンスしてAppendして行きます。

        Label label = new Label("Hello World! From C#");
        root.Add(label);

コレでラベルを付けることが出来ます。label.text を用いれば後からテキストの変更もできます。

        Label label = new Label("Hello World! From C#");
        root.Add(label);
        label.text = "aa";

ボタンをつける

ButtonというクラスがあるのでこれもインスタンスしてAppendします。

        Button button = new Button(() => Debug.Log("aaa"));
        button.text = "Button";
        root.Add(button);

image.png

Buttonコンストラクタの第一引数はイベントです。button.textでボタンのテキストを変更することが出来ます。

入力ボックスを作る

TextFieldコンポネントを利用します。

        TextField textField = new TextField("textField");
        root.Add(textField);

コンストラクタの引数はラベルの名前です。
image.png

入力された値を取るにはtextField.valueを用います。
textField.RegisterValueChangedCallbackを用いて変更時のコールバックを設定することもできます。

        textField.RegisterValueChangedCallback(x => Debug.Log(x.previousValue+ "=>" + x.newValue));

複数行入力に対応したいときは

textField.multilineから入力形式を変更することが出来ます。ついでに style.heightから入力欄の高さを調整しておくとそれっぽいでしょう。

        textField.multiline = true;
//レイアウトにかかわる部分はXX.styleから変更できる
        textField.style.height = 50;

image.png

ドロップダウンを作る

PopUpListというクラスがあるのでそれを使いましょう。

        List<string> choices = new List<string>() { "A", "B", "C" };
        PopupField<string> popupField = new PopupField<string>(choices,0);

image.png

コンストラクタの引数は第一が選択肢、第二が初期値です。
選択中のIdを取るには popupField.index、選択中の値を取るにはpopupField.valueを用います。
また、TextField同様コールバックを登録することもできます。

popupField.RegisterValueChangedCallback(x => Debug.Log(x.newValue));

VisualElementを使う

VisualElementはVisualElementを親として持つことが出来ます。Unityのオブジェクト群やCSS同様にヒエラルキー構造を持たせることが出来るということですね。

        var element = new VisualElement();

試しにmarginを設定してみます。

        element.style.marginTop = 10;
        element.style.marginBottom = 10;
        element.style.marginLeft = 10;
        element.style.marginRight = 10;

ついでにborderLineも付けてみましょう

        element.style.borderColor = new StyleColor(Color.black);
        element.style.borderBottomWidth = 3;
        element.style.borderTopWidth = 3;
        element.style.borderLeftWidth = 3;
        element.style.borderRightWidth = 3;

先ほどまで作っていたUIの親をrootではなくこのelementにしてみます。

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

        Button button = new Button(() => Debug.Log("aaa"));
        button.text = "Button";
        element.Add(button);

        TextField textField = new TextField("textField");
        textField.RegisterValueChangedCallback(x => Debug.Log(x.previousValue+ "=>" + x.newValue));
        element.Add(textField);

        textField.multiline = true;
        textField.style.height = 50;

        List<string> choices = new List<string>() { "A", "B", "C" };
        PopupField<string> popupField = new PopupField<string>(choices,0);
        element.Add(popupField);
        popupField.RegisterValueChangedCallback(x => Debug.Log(x.newValue));

        root.Add(element);

描画結果はこんな感じです。
image.png

親要素であるelementの中に子要素のUIが入っていることがわかると思います。

おわりに

コレでC#オンリーでも雑にUIを組めるようになったのではないでしょうか?明日はUIをもうちょっとマシなレイアウトで表示するための「style」について考えてみましょう。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
3
Help us understand the problem. What are the problem?