きゅぶろぐ

きゅぶんずの ぶろぐができて べんりだな

AnKuchen - UIを作成する際にInspectorにドラッグ&ドロップする必要がなくなります

github.com

セットアップ

  • UnityPackageManagerで「AnKuchen」をインストールします。
  • UIのルートにUICacheComponentを追加し、Updateボタンを押します。
    • 更新ボタンを押す処理をワークフローに合わせて自動化することをオススメします。

f:id:kyubuns:20200903211428p:plain

サンプル

ボタンを取得

GameObjectから名前を指定するだけでコンポーネントを取得することができます。
UICacheの下に「HogeButton」が1つしかない場合、HogeButtonがどこにあっても同じコードで動作します。

public class Sample : MonoBehaviour
{
    [SerializeField] private UICache root = default;

    public void Start()
    {
        var hogeButton = root.Get<Button>("HogeButton");
    }
}

HogeButton の下のテキストを取得する

上の画像を見てください。"Text" という名前のGameObjectが4つあります。
HogeButtonの下にあるTextを取得したい場合は、このようにします。
先ほどと同じように、HogeButtonがRootの直下にあってもなくても動作します。

var hogeButtonText = root.Get<Text>("HogeButton/Text");
hogeButtonText.text = "Hoge!";

ルート直下のテキストを取得する

var text = root.Get<Text>("Text"); // これはエラーになります。"Text"という名前のGameObjectが4つあるからです。
var text = root.Get<Text>("./Text"); // これはルート直下のTextです。

子マップを作成する

UI上の3つのボタンで同じことをしたいとき、
それぞれをマップとして扱うことができます。

public void Start()
{
    var hogeButton = root.GetMapper("HogeButton");
    var fugaButton = root.GetMapper("FugaButton");
    var piyoButton = root.GetMapper("PiyoButton");

    SetButtonText(hogeButton, "Hoge");
    SetButtonText(fugaButton, "Fuga");
    SetButtonText(piyoButton, "Piyo");
}

private void SetButtonText(IMapper button, string labelText)
{
    button.Get<Text>("Text").text = labelText;
}

Duplicate

もっとボタンが欲しい?あなたならできるわ

var newButton = root.GetMapper("HogeButton").Duplicate();
newButton.Get<Text>("Text").text = "New Button!";

複製とレイアウト

ボタンの数を増やした上に、きれいに並べることもできます。

using (var editor = Layouter.TopToBottom(root.GetMapper("HogeButton")))
{
    var newButton1 = editor.Create();
    newButton1.Get<Text>("Text").text = "NewButton1";
    
    var newButton2 = editor.Create();
    newButton2.Get<Text>("Text").text = "NewButton2";
    
    var newButton3 = editor.Create();
    newButton3.Get<Text>("Text").text = "NewButton3";
}

コードテンプレート

UICacheComponentの「Copy Template」ボタンにお気づきでしょうか?
これで誤字脱字の心配はありません。

public void Start()
{
    var ui = new UIElements(root);
    ui.HogeButtonText.text = "Hoge"; // 型があるって素晴らしい!
}

// ↓ ここから下のコードは「Copy Tempalte」を押すと勝手にコピーされるぞ!
public class UIElements : IMappedObject
{
    public IMapper Mapper { get; private set; }
    public GameObject Root { get; private set; }
    public Text Text { get; private set; }
    public Button HogeButton { get; private set; }
    public Text HogeButtonText { get; private set; }
    public Button FugaButton { get; private set; }
    public Text FugaButtonText { get; private set; }
    public Button PiyoButton { get; private set; }
    public Text PiyoButtonText { get; private set; }

    public UIElements(IMapper mapper)
    {
        Initialize(mapper);
    }

    public void Initialize(IMapper mapper)
    {
        Mapper = mapper;
        Root = mapper.Get();
        Text = mapper.Get<Text>("./Text");
        HogeButton = mapper.Get<Button>("HogeButton");
        HogeButtonText = mapper.Get<Text>("HogeButton/Text");
        FugaButton = mapper.Get<Button>("FugaButton");
        FugaButtonText = mapper.Get<Text>("FugaButton/Text");
        PiyoButton = mapper.Get<Button>("PiyoButton");
        PiyoButtonText = mapper.Get<Text>("PiyoButton/Text");
    }
}

型があってもDuplicate

タイプがあってもDuplicateやLayouterは使えます。安心してください。

public void Start()
{
    var ui = new UIElements(root);
    using (var editor = Layouter.Edit(ui.HogeButton))
    {
        foreach (var a in new[] { "h1", "h2", "h3" })
        {
            var button = editor.Create();
            button.Text.text = a;
        }
    }
}

public class UIElements : IMappedObject
{
    public IMapper Mapper { get; private set; }
    public GameObject Root { get; private set; }
    public Text Text { get; private set; }
    public ButtonElements HogeButton { get; private set; }

    public UIElements(IMapper mapper)
    {
        Initialize(mapper);
    }

    public void Initialize(IMapper mapper)
    {
        Mapper = mapper;
        Root = mapper.Get();
        Text = mapper.Get<Text>("./Text");
        HogeButton = mapper.GetChild<ButtonElements>("HogeButton");
    }
}

public class ButtonElements : IMappedObject
{
    public IMapper Mapper { get; private set; }
    public GameObject Root { get; private set; }
    public Button Button { get; private set; }
    public Text Text { get; private set; }

    public void Initialize(IMapper mapper)
    {
        Mapper = mapper;
        Root = mapper.Get();
        Button = mapper.Get<Button>();
        Text = mapper.Get<Text>("Text");
    }
}

翻訳

たまにはTextをまとめてメッセージを入れたい時もありますよね。

root.SetText(new Dictionary<string, string>
{
    { "./Text", "Title" },
    { "HogeButton/Text", "Hoge" },
    { "FugaButton/Text", "Fuga" },
    { "PiyoButton/Text", "Piyo" },
});

テスト

タイプを作った後に、プレハブを変えた人がいた!?
そんな事故を未然に防ぎましょう。

var test1Object = Resources.Load<GameObject>("Test1");
Assert.DoesNotThrow(() => UIElementTester.Test<UIElements>(test1Object));

インストール

  • パッケージマネージャ https://github.com/kyubuns/AnKuchen.git?path=Unity/Assets/AnKuchen
  • 更新するには、Packages/packages-lock.json の Hash を書き換えてください。

github.com