Communityコーディング&開発github.com

MericKarabulut/unity-ui-toolkit

UnityでUI Toolkit(UXML + USS + C#コントローラー)を使ってゲームUIを構築・デバッグするためのスキル。HUD、ヘルス/アモ/スタミナバー、インタラクションプロンプト、ダメージビネット、メニュー、ポーズ画面、スコア表示、カットシーン画面など、あらゆるジャンルとプラットフォームに対応。

unity-ui-toolkit とは?

このスキルは、UnityのUI Toolkit(UXML + USS + C#コントローラー)を用いてゲームUIを効率的に構築・デバッグするための包括的なガイドです。PanelRendererワークフロー(UIDocument不使用)と従来のUIDocumentワークフローの両方をサポートし、HUD、メニュー、スコア表示などあらゆるゲームUI部品の作成に利用できます。スキルの核となるのは、スタイルガイドを先に作成して一貫性を保つこと、1部品=1コントローラ+1UXML+1USSというアーキテクチャ、そしてUSSのクラス切り替えとトランジションで状態とアニメーションを管理する手法です。さらに、レイアウトのバグ(特に絶対配置の重なり)を防ぐ実践的なテクニックや、UI Builder、USS、UXMLに関するよくあるトラブルとその修正方法を多数収録しており、プロジェクトの時間を大幅に節約できます。このスキルは、UnityでUIに関する言及があった場合(UI Toolkit, UXML, USS, PanelRenderer, UIDocument, UI Builderという単語がなくても、HUDやヘルスバー、メニューなど)、常に使用すべき強力なリファレンスです。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/MericKarabulut/claude-skills/tree/HEAD/skills/unity-ui-toolkit

Installed? Explore more コーディング&開発 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Unity UI Toolkit ゲームUI

UXMLで構造、USSで見た目、ゲームイベントに反応する小さなC#コントローラーで動作を定義します。実プロジェクト(ホラーシューターHUD)から抽出した内容です。特にトラブルシューティングリストは時間節約に最も役立ちます。

1. コードを書く前に

不足している点だけを、一度の短いバッチで質問してください:

  • UI API. [RequireComponent(typeof(PanelRenderer))] + RegisterUIReloadCallback = PanelRendererワークフロー(UIDocumentなし、rootVisualElementなし)。UIDocument + rootVisualElement = 従来の方式。貼り付けられたコードから推測します。不明な場合は、動作しているコントローラーとそのUXMLを1つ尋ねてください。決して両方を混在させないでください(NullReferenceExceptionの原因になります)。動作実績のあるファイルは、APIの記憶よりも優先されます。
  • ジャンル/テーマ(スクリーンショットやパレットがあれば)、プラットフォームと入力方式、基準解像度/アスペクト比。
  • UIがリッスンするゲームスクリプト。 それらを要求し、実際のpublicイベント/プロパティを読み、それらにバインドしてください。イベントがない場合は、ゲームスクリプトへの最小限の追加を提案してください。
  • ユーザーの言語で返信し、既存のスクリプトが使用している言語でコードコメントを記述してください。

2. スタイルガイドを先に

ユーザーが最初に気付くのは一貫性です。多くの部品を作成する前に:短いガイドを提案し、1つの小さな部品を作成してユーザーの反応を待ち、そのフィードバックをルールに変換します(「背景ボックスなし」「もっとコンパクトに」)。それをプロジェクトに保存し(例:Assets/UI/UI_STYLE_GUIDE.md)、将来のUIリクエストに貼り付けるようにユーザーに伝えます。部品やルールが変更されたら更新します。内容:デザイン言語(トーン、コンテナ、密度、モーション)、パレットテーブル(テキスト、ミュート、警告、クリティカル、シャドウ)、タイポグラフィ(ロールごとのサイズ、シャドウルール)、レイアウト(ゾーン、マージン、Panel Settings)、USSの命名規則と状態クラス、コードアーキテクチャルール、UXMLの注意点、既存部品のリスト、新規部品のチェックリスト。

3. アーキテクチャ

  • 1部品 = 1コントローラー + 1UXML + 1USS、データソースは1つ。巨大コントローラーは禁止。
  • 一緒に配置する必要がある部品は、1つのUXML/flexコンテナを共有します(2つのコントローラーが1つのGameObject/PanelRendererを共有しても構いません。各コントローラーは名前で自身の要素を見つけます)。
  • コントローラーは要素を見つけ、テキスト/値を設定し、USSクラスを切り替えるだけです。色、サイズ、アニメーションはUSSに記述します。
  • PanelRenderer:ルートは RegisterUIReloadCallback(OnUIReload) を介して取得します(OnEnableで登録、OnDisableで解除)。UIの準備ができたときと、リロードのたびに呼び出されるため、すべての Q<T>("name") 検索、キャッシング、イベントバインディング、状態リセットはここで行います。Update() では要素が存在するまでnullガードします。
  • ゲームイベントはUIが存在する前に発生する可能性があります:最新の値をフィールドにキャッシュし、OnUIReload の最後で再適用します。
  • プレイヤー/ゲームオブジェクトは遅れてスポーンする可能性があります:シリアライズされた参照を使用するか、FindFirstObjectByType をスロットル(約0.5秒)してからバインドし、OnDisableで購読解除します。
  • プッシュ型で、ポーリングはしません。イベントを購読し、変更があったときだけ書き込みます。イベントがない値(継続的なスタミナ、リロードbool)のみポーリングし、変更時にクラスを切り替えます。フレームごとの文字列構築は禁止。
  • 繰り返し要素(ピップ、セグメント)はコードで一度だけ構築し、ソースが変更されたときにのみ再構築します。リロード時にキャッシュリストをクリアします。
  • タイマー/パルス:element.schedule.Execute(...).Every(ms) は一時停止状態で作成し、必要な間だけ再開します。ワンショットフラッシュ:.StartingIn(ms)。
  • HUD要素:picking-mode="Ignore" をすべてに設定し、フォーカス可能なコントロールは使用しません。メニューは例外です。
  • ファイル名 = MonoBehaviourのクラス名。

4. レイアウト(ほとんどのビジュアルバグの原因)

  • 関連する部品を個別に推測したピクセルオフセットで配置しないでください:2つの絶対パネルが bottom の盲目的な推測により重なっていました。それらを1つのflexコンテナに入れ、そのコンテナのみを配置します。
  • 画面ゾーンごとに1つの絶対配置コンテナを使用し、子要素はflexとマージンでフローさせます。
  • レイアウトがジャンプしてはいけない場合は opacity + トランジションで非表示にします。display:none はスペースを折りたたむ必要がある場合のみ使用します。
  • 数値ラベルには固定 width を設定し、9から10、100に変わっても隣の要素がずれないようにします。
  • ボックスはborder-box:セグメント行の幅 = count x (width + margin) - margin。関連するウィジェットは、同一の左端と計算された幅で位置を揃えます。
  • Panel Settings:Scale With Screen Size(例:1920x1080、match 0.5)。HUDの場合はClear ColorとClear Depth StencilをOFFにします。PanelRendererのSort Orderを使用して部品をレイヤー化します(全画面ビネットはテキストの下に)。

5. USSと状態

  • 状態 = C#から切り替えるクラス(EnableInClassList)。アニメーション = USSトランジション。コード内で色を設定したりアニメーションさせたりしないでください。
  • 状態は意味で命名:--low、--critical、--hidden、--hit、--pulse、--unavailable。新しい部品ではBEMを推奨します。
  • オーバーライドルール(使用済みセグメント、フラッシュ)は、上書きするルールの後に配置します。同一の詳細度で、最後のルールが優先されます。
  • トランジションは、行き先の状態のduration/delayを使用します:.x--hidden { opacity:0; transition-duration:.5s; transition-delay:.9s; } は遅れてフェードアウトし、即座にフェードイン。.x--flash { transition-duration:.05s; } は即座にオンになり、基本速度でフェードオフ。
  • 背景なしで浮かぶテキストには text-shadow が必要です。text-transform は使用しないでください(C#で大文字化)。ラジアルグラデーションは使用しないでください(ビネットを参照)。
  • スキン変更可能なUIの場合は、色/サイズをルートクラスのUSS変数(--c-text、var(--c-text) で使用)に配置します。ユーザーのUnityバージョンがサポートしているか確認してください。

6. コントローラースケルトン(PanelRenderer)

[RequireComponent(typeof(PanelRenderer))]
public class PartController : MonoBehaviour
{
    [SerializeField] private SourceComponent source;      // null = 実行時に検索
    private PanelRenderer _pr; private VisualElement _block; private Label _value;
    private bool _bound; private float _v = -1f; private int _shown = -1; private float _nextFind;

    private void Awake() => _pr = GetComponent<PanelRenderer>();
    private void OnEnable()  { _pr.RegisterUIReloadCallback(OnUIReload); TryBind(); }
    private void OnDisable() { _pr.UnregisterUIReloadCallback(OnUIReload); Unbind(); _block = null; _value = null; }

    private void Update()
    {
        if (_bound || Time.unscaledTime < _nextFind) return;
        _nextFind = Time.unscaledTime + 0.5f; TryBind();
    }
    private void TryBind()
    {
        if (_bound) return;
        if (source == null) source = FindFirstObjectByType<SourceComponent>();
        if (source == null) return;
        source.OnChanged += OnChanged; _bound = true;
        _v = source.Current; Apply();                      // 現在の状態を一度取得
    }
    private void Unbind() { if (_bound && source != null) source.OnChanged -= OnChanged; _bound = false; }

    private void OnUIReload(PanelRenderer r, VisualElement root)
    {
        _block = root.Q<VisualElement>("part"); _value = root.Q<Label>("part-value");
        if (_block == null || _value == null) { Debug.LogError("Part: elements not found in UXML (check Source Asset).", this); _block = null; return; }
        _shown = -1; Apply();                              // 新しいツリー:キャッシュから強制書き換え
    }
    private void OnChanged(float v) { _v = v; Apply(); }
    private void Apply()
    {
        if (_block == null || _v < 0f) return;
        int n = Mathf.CeilToInt(_v);
        if (n != _shown) { _shown = n; _value.text = n.ToString(); }   // 変更時のみ書き込み
        _block.EnableInClassList("part--low", _v <= 0.3f);
    }
}

7. 再利用可能なスニペット

  • セグメント/ピップ: OnUIReload 内で row.Clear(); list.Clear(); を実行し、pickingMode=Ignore の VisualElement をN個、クラス seg で追加します。変更時は list[i].EnableInClassList("seg--spent", i >= filled)。値 > 0 の間は最低1つはfilledを表示:filled = v<=0 ? 0 : Clamp(CeilToInt(v*N-0.001f),1,N)。
  • パルス: _pulse = _block.schedule.Execute(() => _block.ToggleInClassList("part--pulse")).Every(400); _pulse.Pause(); 状態に入るときに再開し、離れるときにPause + クラス削除。_pulsing boolを追跡し、リロード時にリセットします。
  • イベント時のフラッシュ: var b=_block; b.AddToClassList("part--hit"); b.schedule.Execute(()=>b.RemoveFromClassList("part--hit")).StartingIn(150);
  • ビネット: Awakeで一度だけ128x128の Texture2D(RGBA32、Clamp、Bilinear、HideFlags.HideAndDontSave)を生成します。アルファは中心からの距離に基づき SmoothStep(0,1,InverseLerp(inner,outer,dist/1.4142))、RGBは白にします。OnUIReload で _el.style.backgroundImage = new StyleBackground(tex) を設定し、OnDestroyで破棄します。USS:全画面絶対要素、opacity:0、-unity-background-scale-mode: stretch-to-fill、-unity-background-image-tint-color で色を設定、クラス --low/--critical/--pulse/--flash でopacityを設定(flashは最後、高速duration)。テキスト部品より低いSort Orderにします。

8. ジャンルとプラットフォーム

アーキテクチャはジャンル間で同一です。スタイルガイドと入力モデルが変わります。アーケード:大きくてごつい数字、カウントアップ、スケールポップ、コンボグロー、パンチの効いた0.08〜0.25秒のトランジション、コンテナを歓迎。レトロ/ピクセル:ピクセルフォント、整数スケーリング、Pointフィルターテクスチャ、スプライト9スライスボーダー。モバイル:タッチターゲット約44〜48dp、デバイスのセーフエリアをルートパディングとして適用(ユーザーのUnityバージョンに適したAPIを確認)、ホバー状態なし、大きめの最小フォント、実機でテスト。コンソール/ゲームパッド:フォーカス可能な要素と明確な :focus スタイル、明示的な初期フォーカス、TVセーフマージン(3〜5%)、大きめのテキスト、アクティブなデバイスに応じて切り替わるグリフ。VR/ワールドスペース:Unityバージョンに依存するため、まず調べてください。これらのプラットフォームポイントは一般的な良いプラクティスですが、実プロジェクトでテストされていません。その旨を伝え、ユーザーに実機で確認するよう依頼してください。入力ラベル(キー名またはグリフ)は、コントローラーが読み取るデータとして保持し、UXMLにハードコードしないでください。

9. 部品の納品

順序:完全なUXML、完全なUSS、完全なコントローラー、セットアップ手順(GameObject、PanelRenderer + 共有Panel Settings + Source Asset、Sort Order、埋めるべき参照、Playで確認すべきこと)、チューニング箇所(どのUSS値がサイズ/位置/色/閾値を変更するか)、および短いスタイルガイドエントリ。既存ファイルを変更する場合、ファイルの大部分が変わる場合はファイル全体を提示してください(部分的な貼り付けは実際のエラーを引き起こしました)。

10. トラブルシューティング

症状原因 / 修正方法
rootVisualElement / GetComponent<UIDocument>() でNullReferenceコードがUIDocumentを使用しているが、オブジェクトにはPanelRendererがある。RegisterUIReloadCallback を使用し、OnUIReload 内でバインドする
コントローラーが「要素が見つかりません」とログ出力Source Assetが間違っている、Q<>() と名前が一致しない、またはUXMLが無効
UI Builderで「Failed to open asset」無効なUXML(重複した </ui:UXML>、不正な <Style>、プレースホルダーGUID、不可視文字)。修正後、ファイルを右クリック > Reimport。汎用エラーの上のコンソールメッセージを読む
スタイルが適用されない<Style> がない、またはパスが間違っている。UI Builderで:StyleSheets > + > Add Existing USS(project://database/...guid=... という完全パスが書き込まれる)
編集後すべてが消えたUXMLが壊れた、または名前変更後にSource Assetの参照が古くなった。修正、Reimport、再割り当て。ルートに background-color: rgba(255,0,0,.3) を適用してテスト
部品が別の部品とずれている、または重なっている古いUXMLを描画している古いGameObjectが残っている、または個別の絶対オフセット。古いオブジェクトを削除し、隣接する部品を1つのコンテナにマージ
Gameビューにプレースホルダーテキストが表示される編集モードではUXMLが描画されます。Playを押す
スクリプトを追加できないファイル名 != クラス名。UnityのProjectウィンドウ内で名前を変更し、.meta が追随するようにする

有効でない修正方法:スタイルシートタグは <Style src="..."/> であり、ui: プレフィックスは不要(<ui:Style> は黙って無視されます)。プレースホルダーGUIDを発明したり残したりしないでください。標準ヘッダーを使用:<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">。

11. エディタを見ずに作業する

レイアウトの不具合についてはスクリーンショットを要求し、それを注意深く読んでください。これにより、推測では解決できなかったバグを解決できました。自分のミスは簡潔に認め、完全なファイルで修正します。コンパイルエラーについては、再推測する代わりに、正確なテキストとUnityバージョンを要求してください。デバイスの挙動やパフォーマンス値は約束しないでください。構造を作成し、ユーザーにテストを依頼してください。

関連スキル