【Unity】UI Toolkit入門:uGUIとの使い分けと、ランタイムUIの作り方

作成: 2026-07-16

uGUIは使える。でもUnity 6時代に存在感を増すUI Toolkitは、いつ選べばいいのか——結論は「置き換え」ではなく「使い分け」です。UIDocument・UXML・USSの役割分担、UI Builderで作った画面をC#から操作する方法、USSクラスによる状態の切り替え、ListViewの仮想化まで、クエストログ画面を題材にランタイムUIの作り方を解説します。uGUIを残すべき場面も明確にします。

Canvasとアンカー を覚え、Layout Group で並べて、uGUIのUIは一通り作れるようになった。そこへ聞こえてくるのが「これからは UI Toolkit らしい」という声です。じゃあuGUIで作った知識は無駄になるのか? 全部作り直し?——安心してください。答えは 「置き換え」ではなく「使い分け」 です。

この記事では、UI Toolkitの部品(UIDocument・UXML・USS)の関係から、UI Builderで作った画面をC#から動かす方法、そして本領である 大量リストの仮想化 までを、クエストログ画面を題材に解説します。最後に「uGUIを残すべき場面」も明確にします。

UXMLとUSSとC#でUIを組み立てるイメージ

この記事でわかること

  • uGUIとUI Toolkitの 使い分けの判断基準
  • UIDocument・UXML・USS の役割分担(構造・見た目・振る舞い)
  • UI Builderで作った要素を C#から Q<T>() で掴む
  • USSクラスの付け外し で状態の見た目を切り替える
  • ListViewの仮想化(1000件でも軽いリスト)
  • 実践:クエストログ画面を組む
  • uGUIを残す場面(World Space・演出・既存資産)

動作確認環境: Unity 2022.3 LTS / Unity 6

Sponsored

uGUIとUI Toolkitは、競合ではない

2つのUIシステムは、生まれも得意分野も違います。

  • uGUI はGameObjectベースです。UIの1要素が1つのGameObjectで、Transformで動かせて、パーティクルや3D演出と自由に混ざる。 「ゲームの世界に溶け込むUI」 が得意です
  • UI Toolkit はWebライクです。構造(UXML)と見た目(USS)をファイルに分け、要素はGameObjectではない軽量なオブジェクト。 「大量のデータを整然と並べる画面」 が得意で、エディタ拡張のUIもこれで作ります
uGUIとUI Toolkitの選択フロー図。ワールド空間に置くUIや演出と密結合のUIはuGUI、大量リスト・データ駆動の画面・エディタ拡張はUI Toolkitへ分岐する

判断の目安はこうです。 キャラの頭上のHPバー、3D空間の看板、アニメーションと絡む演出UI → uGUIインベントリ・ランキング・クエストログのような「表」、設定画面のような「フォーム」、エディタ拡張 → UI Toolkit。1つのゲームの中で両方を使うのは、まったく普通のことです。

部品の関係:UIDocument・UXML・USS

UI Toolkitの画面は、3種類のファイルと1つのコンポーネントでできています。Web開発の経験があれば「HTML・CSS・JavaScript」の関係そのものです。

UXML・USS・C#の責務分担図。UXMLが構造、USSが見た目、C#が振る舞いを担当し、UIDocumentコンポーネントがそれらをシーンにつなぐ
部品役割Webでの相当物
UXML(.uxml)構造:何がどの順で並ぶかHTML
USS(.uss)見た目:色・余白・フォントCSS
C#振る舞い:クリックで何が起きるかJavaScript
UIDocument上記をシーンに接続するコンポーネント

シーン側の準備は、空のGameObjectに UIDocument コンポーネントを付け、作ったUXML(Source Asset)と Panel Settings アセットを割り当てるだけです。Canvasのような階層は要りません——画面の構造はUXMLファイルの中にあります。

UI Builderで作って、C#から掴む

UXMLを手書きする必要はありません。 UI Builder(Window > UI Toolkit > UI Builder)が、uGUIのSceneビューに相当するビジュアルエディタです。左のLibraryからLabelやButtonをドラッグし、右のInspectorで名前とスタイルを整えます。

作った要素をC#から掴むときの合言葉が Q<T>()(Query)です。

using UnityEngine;
using UnityEngine.UIElements;

public class TitleScreen : MonoBehaviour
{
    [SerializeField] private UIDocument document;

    void OnEnable()
    {
        // UIの根っこ(rootVisualElement)から、名前で要素を探す
        VisualElement root = document.rootVisualElement;

        Button startButton = root.Q<Button>("start-button");
        Label versionLabel = root.Q<Label>("version-label");

        versionLabel.text = "v" + Application.version;
        startButton.clicked += OnStartClicked;
    }

    void OnStartClicked()
    {
        Debug.Log("ゲーム開始!");
    }
}

uGUIとの決定的な違いに気づいたでしょうか。 InspectorでボタンをSerializeFieldにドラッグする配線が消え、名前による検索に変わっています。UIの構造を変えても、名前さえ守ればC#は壊れません。

Sponsored

USSクラスで状態を切り替える

USSは見た目のルール集です。要素に クラス.quest-item のような名前札)を付けておき、ルールを紐づけます。

/* QuestLog.uss */
.quest-item {
    padding: 6px 12px;
    color: rgb(230, 230, 230);
}

/* 達成済みクエストの見た目:クラスを1つ足すだけで切り替わる */
.quest-item--done {
    color: rgb(120, 200, 120);
    -unity-font-style: italic;
}

/* マウスが乗った時(擬似クラス) */
.quest-item:hover {
    background-color: rgba(255, 255, 255, 0.08);
}

C#側は「見た目の詳細」を知らずに、 クラスを付け外しするだけ です。

// 達成状態に応じてクラスを付け外しする(第2引数がtrueなら付く)
questLabel.EnableInClassList("quest-item--done", quest.isDone);

uGUIなら「色を変えるコード」「フォントを変えるコード」をC#に書いていた場面が、 「状態の名前を貼るだけ」 になります。見た目の調整はUSSファイル側で完結し、デザイン変更でC#を触る必要がなくなる——これがUI Toolkitの設計思想の中心です。

イベントと、ListViewの仮想化

イベントは clicked(Button専用の簡易版)と RegisterCallback(汎用)の2系統があります。どちらも 登録したら解除する のは、C#イベント と同じ作法です。

そしてUI Toolkitの本領が ListViewの仮想化 です。

ListViewの仮想化の図。1000件のデータリストに対して、画面に見えている行の分だけ要素が生成され、スクロールすると同じ要素が新しいデータで使い回される

uGUIで1000件のリストを素直に作ると、1000個のGameObjectが生まれて画面外の分まで毎フレーム面倒を見ることになります。ListViewは発想が逆で、 画面に見えている十数行だけを作り、スクロールに合わせて中身を差し替えて使い回します。1000件でも10000件でも、生成されるのは見えている行数分だけです。

使い方は3点セットです。

listView.makeItem = () => new Label();        // 行の器を1つ作る方法
listView.bindItem = (element, index) =>       // 器にi番目のデータを流し込む方法
{
    ((Label)element).text = quests[index].title;
};
listView.itemsSource = quests;                // データ本体

「器の作り方」と「データの流し込み方」だけ教えれば、生成数の管理と使い回しはListViewが引き受けます。

実践:クエストログ画面を組む

RPGのクエストログ、ソシャゲのミッション一覧、実績画面——「左に一覧、選ぶと詳細」はゲームUIの定番構図です。ListViewの練習台として最適なので、これを組みます。

クエストログ画面の場面図。左側にクエスト名が並ぶリスト(達成済みは色が変わっている)、右側に選択中クエストの詳細説明が表示されている

UXML(UI Builderで組んだ構造。左にリスト、右に詳細):

<ui:UXML xmlns:ui="UnityEngine.UIElements">
    <ui:VisualElement name="quest-log" class="quest-log">
        <ui:ListView name="quest-list" class="quest-list" />
        <ui:Label name="quest-detail" class="quest-detail" text="クエストを選択" />
    </ui:VisualElement>
</ui:UXML>

C#(データを流し込み、選択に反応する):

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UIElements;

[Serializable]
public class Quest
{
    public string title;
    public string detail;
    public bool isDone;
}

public class QuestLogScreen : MonoBehaviour
{
    [SerializeField] private UIDocument document;
    [SerializeField] private List<Quest> quests = new List<Quest>();

    private ListView questList;
    private Label questDetail;

    void OnEnable()
    {
        VisualElement root = document.rootVisualElement;
        questList = root.Q<ListView>("quest-list");
        questDetail = root.Q<Label>("quest-detail");

        // 3点セット:器の作り方・流し込み方・データ本体
        questList.makeItem = () => new Label();
        questList.bindItem = (element, index) =>
        {
            var label = (Label)element;
            label.text = quests[index].title;
            label.AddToClassList("quest-item");
            // 達成済みなら見た目クラスを付ける(詳細はUSSに任せる)
            label.EnableInClassList("quest-item--done", quests[index].isDone);
        };
        questList.fixedItemHeight = 32;
        questList.itemsSource = quests;

        // 行を選んだら詳細を出す
        questList.selectionChanged += OnQuestSelected;
    }

    void OnDisable()
    {
        // 解除はいつもの作法
        questList.selectionChanged -= OnQuestSelected;
    }

    void OnQuestSelected(IEnumerable<object> selection)
    {
        if (selection.FirstOrDefault() is Quest quest)
        {
            questDetail.text = quest.title + "\n\n" + quest.detail;
        }
    }
}

InspectorでQuestを20件ほど登録してPlayしてみてください。一覧が並び、クリックで詳細が切り替わり、達成済みだけ緑のイタリックになります。ここで一度、 Questを1000件に増やしてみてください——スクロールは滑らかなままのはずです。試しにヒエラルキーを見ても、1000個のGameObjectはどこにもありません。それが仮想化です。

もし一覧が空っぽなら、見る場所は2つ。itemsSource を設定する前に Q<ListView>null を返していないか(UXML側の name とC#の文字列が一致しているか)。そして fixedItemHeight が0のままだと行の高さが計算できず表示されないことがあります。

ポイントは2つ。 C#はデータと状態クラスだけを扱い、見た目はUSSに任せる大量リストはListViewの3点セット(makeItem・bindItem・itemsSource)に乗せるだけで仮想化が付いてくる。

uGUIを残す場面

UI Toolkitが便利でも、全部を移す必要はありません。次の場面はuGUIが今も定番です。

  • World Space UI: キャラの頭上のHPバー、3D空間に置く看板、VRのUI。UI Toolkitのランタイム対応はスクリーンスペース中心で、ワールド空間はuGUIのWorld Space Canvasの領分です
  • 演出と密結合のUI: パーティクルを重ねる、UIそのものをAnimatorで暴れさせる、3Dモデルを混ぜ込む——GameObjectであることが利点になるUI
  • 既存資産: 動いているuGUI画面を書き換える理由は、基本ありません。 新しく作る「表とフォーム」からUI Toolkitを試す のが現実的な移行です

おまけ:先に知っておくと良いこと

  • 解像度対応はPanel Settingsで: uGUIのCanvas Scalerに相当する設定(Scale ModeのScale With Screen Size相当)はPanel Settingsアセットにあります。基準解像度を決めておけば、リサイズ対応はUSSのパーセント指定と合わせて素直に効きます
  • ゲームパッド対応はフォーカスの設計: ボタン間の移動はフォーカスシステムが担います。マウス前提で作ってからパッド対応を足すと苦労するので、パッド対象のゲームなら最初からフォーカス移動を確認しながら組んでください
  • エディタ拡張と地続き: ここで覚えたUXML・USS・Q<T>() は、そのまま エディタ拡張 のUI作りに使えます。ランタイムUIの練習が、開発ツール製作の練習を兼ねます

まとめ

  • uGUIとUI Toolkitは 使い分け:世界に溶け込むUIはuGUI、表とフォームと大量リストはUI Toolkit
  • 分担は UXML=構造・USS=見た目・C#=振る舞い、つなぐのが UIDocument
  • C#からは Q<T>("name") で掴む。Inspector配線は名前検索に置き換わる
  • 状態の見た目は USSクラスの付け外しEnableInClassList)で切り替える
  • 大量リストは ListViewの3点セット で仮想化——1000件でもGameObjectは増えない

次にインベントリやランキングのような「表」を作る日が来たら——uGUIで組み始める前に、この記事のクエストログを思い出してください。それがUI Toolkitの出番です。