Unityのツールバーにボタンを追加する方法(Unity 2021.2以降)

[PONOS Advent Calendar 2023](https://qiita.com/advent-calendar/2023/ponos) の12日目の記事です。

# はじめに
[二年前の記事](https://qiita.com/iixd_pog/items/f9536aefb2f5c00c3913)で特定シーンの再生ボタンを追加する方法を記載しましたが、Unity2021.2以降のUI　ToolKitが導入されて以降、動作しなくなっておりました。
こちらの解決を行うための記事となります。
シーン再生部分は変わらないため、ボタンの表示と調整方法について記載致します。
また、UI ToolKitのデバッグ方法も記載致します。

# 対象者
最新のUnityで自分だけのボタンや拡張を入れたい人向けになります。

# UI ToolKitとはなにか
[UI Toolkit](https://docs.unity3d.com/ja/2022.3/Manual/UIElements.html)とは、UnityのUIを作成する上で使用できる機能の１つです。
UI作成、エディタ拡張など幅広い範囲で使用でき、HTMLなどのウェブベースで作成できます。

# ツールバーにボタンを追加する
### 実装
#### ツールバーを取得する設定
ツールバーのタイプを取得し保持しておくことで処理負荷を減らしています。
```csharp:CustomToolbar.cs
/// <summary>
/// UnityEditor.ToolbarのType取得
/// </summary>
static readonly System.Type ToolbarType = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.Toolbar");
```

#### エディタの更新に登録
スクリプトのコンパイル後にエディタの更新にイベントを登録することで、毎回表示されるようにしております。
```csharp:CustomToolbar.cs
/// <summary>
/// スクリプトコンパイル後に呼び出せる処理
/// </summary>
[InitializeOnLoadMethod]
static void InitializeOnLoad()
{
    EditorApplication.update -= OnUpdate;
    EditorApplication.update += OnUpdate;
}
```

#### ツールバーの取得と保持
ツールバーの生成がずれる場合があるため、Updateでチェックしております。
```csharp:CustomToolbar.cs
/// <summary>
/// 現在描画中のツールバー
/// </summary>
static ScriptableObject currentToolbar = null;
        
/// <summary>
/// Editorの描画更新に合わせて描画を更新する処理
/// </summary>
static void OnUpdate()
{
    if (currentToolbar == null)
    {
        var toolbars = Resources.FindObjectsOfTypeAll(ToolbarType);
        currentToolbar = toolbars.Length > 0 ? (ScriptableObject)toolbars[0] : null;
        if (currentToolbar != null)
        {
            AddHandler(currentToolbar);
        }
     }
}
```
#### 描画設定
VisualElementを取得して、IMGUIContainerを設定できるようにしています。
VisualElementの取得としてツールバーの型から取得し、Q関数を使うことでツールバーのどこに表示させるかを決定できます。
| 設定名 | 描画 |
|:-----------|------------:|
| ToolbarZoneLeftAlign | ツールバーの左のVisualElementを取得 |
| ToolbarZonePlayMode | プレイモードのVisualElementを取得 |
| ToolbarZoneRightAlign | ツールバーの右のVisualElementを取得 |

```csharp:CustomToolbar.cs
/// <summary>
/// ツールバーに描画ハンドラーの追加処理
/// </summary>
/// <param name="toolbar">描画されているツールバー</param>
static void AddHandler(object toolbar)
{
    FieldInfo root = toolbar.GetType().GetField("m_Root", BindingFlags.NonPublic | BindingFlags.Instance);
    VisualElement concreteRoot = root.GetValue(toolbar) as VisualElement;
    // 左に表示
    VisualElement toolbarZone = concreteRoot.Q("ToolbarZoneLeftAlign");
    VisualElement parent = new VisualElement()
    {
        name = "ボタン名",
        tooltip = "ボタンの説明",
        style = {
                    flexGrow = 1,
                    flexDirection = FlexDirection.RowReverse,
                }
    };
    IMGUIContainer container = new IMGUIContainer();
    container.onGUIHandler += OnToolbarGUI;
    parent.Add(container);
    toolbarZone.Add(parent);
}
/// <summary>
/// ツールバー更新処理
/// </summary>
static void OnToolbarGUI()
{
    using (new GUILayout.HorizontalScope())
    {
        if (GUILayout.Button(EditorGUIUtility.IconContent("UnityEditor.GameView"),
                new GUIStyle("Command")
                {
                    fontSize = 16,
                    alignment = TextAnchor.MiddleCenter,
                    imagePosition = ImagePosition.ImageAbove,
                    fontStyle = FontStyle.Bold
                }
        ))
        {
            //ボタンを押されたときの処理
        }
    }
}
```
# 描画結果
描画の結果として、以下のように表示されます。
ボタンを押すと登録された処理が実行されます。
![スクリーンショット 2023-12-05 18.01.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2334295/d1042050-45a8-c754-b4d7-2a313ef0d8b6.png)

# UI Toolkitのデバッグを行う方法
Unityのツールバーにある「Window -> UI Toolkit -> Debugger」からデバッグ用の画面を開くことができます。
![スクリーンショット 2023-12-06 19.33.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2334295/13df430f-8e5a-8123-9088-ce7afe0e2aae.png)

UI Toolkit Debuggerのタブの下にあるプルダウンから、「Toolbar」や「SceneView」を選ぶことでどのような表示や値の確認、設定を変更しての見え方の調整が行なえます。
こちらを駆使することで理想的なUIを作成しましょう！

# まとめ
2021年の記事の内容を改修する形で記載致しましたが、UI　Toolkitにはまだまだやれることが多いため、新しいUIを設定する際やNode Graphの作成をする際に触ってみてください。
きっと楽しく、便利になると思います！
ありがとうございました。

それでは、次回は@FW14Bさんの記事です。
お楽しみに！