Unity ui
CLI tool for controlling Unity Editor via command line - Play Mode, console logs, tests, scenes, GameObjects and more
npx -y skills add bigdra50/unity-cli --skill unity-uiAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
What its author says it does
Copied from the file, not written here
UI 開発・検査・自動テストワークフロー。uitree で構造把握→pytest E2E テスト生成→PlayMode テストに移植。 Use for: "UI確認", "UI Toolkit", "uGUI", "E2Eテスト", "UI操作", "uitree", "スクリーンショット"
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
15.9 KB, as published. Nobody here has run it
unity-ui
PREREQUISITE:
../unity-shared/SKILL.md(Relay Server 経由で Unity Editor が起動/アクティブであること)skill 経由のコマンドは必ず
-i <instance>を付ける (unity-shared #インスタンス指定)。uitree コマンドは Play Mode 中のみ動作する。必ず
u -i <instance> playしてから実行すること。
UI テストの種別
| 種別 | 検証内容 | 手法 | スクショ |
|---|---|---|---|
| Functional | 操作→状態が正しいか | click → text で値検証 | 不要 |
| Visual Regression | 外観が変わっていないか | screenshot → 画像比較 | 必要 |
| Structural Snapshot | ツリー構造が変わっていないか | dump --json → diff | 不要 |
| Smoke | 最低限動くか | click → console にエラーなし | 任意 |
| Monkey | ランダム操作でクラッシュしないか | monkey → errors 確認 | 不要 |
UI システム判定
| 手がかり | UI Toolkit | uGUI |
|---|---|---|
| ファイル | .uxml, .uss | .prefab に Canvas |
| コンポーネント | UIDocument | Canvas, RectTransform |
コマンドリファレンス
要素の指定方法は2通り: ref ID (ref_3) または -p <panel> -n <name>。
構造把握
u -i <instance> uitree dump # 全パネル一覧 (contextType: Player がゲーム側)
u -i <instance> uitree dump -p "PanelSettings" # パネルのツリー (各要素の ref/name/type/classes が見える)
u -i <instance> uitree dump -p "PanelSettings" --json # JSON 出力 (snapshot用)
u -i <instance> uitree query -p "PanelSettings" -t Button # type で検索 (VisualElement ベースのボタンはヒットしない → -c で検索)
u -i <instance> uitree query -p "PanelSettings" -n "BtnStart" # name で検索
u -i <instance> uitree query -p "PanelSettings" -c "action-btn" # USS class で検索
u -i <instance> uitree query -p "PanelSettings" -t Button -c "primary" # AND 条件
u -i <instance> uitree inspect -p "PanelSettings" -n "BtnStart" # 要素詳細
u -i <instance> uitree inspect ref_5_48 # ref ID で指定
u -i <instance> uitree inspect ref_5_48 --style # resolvedStyle 含む
u -i <instance> uitree inspect ref_5_48 --children # 子要素情報含む
テキスト取得
u -i <instance> uitree text -p "PanelSettings" -n "ToastMessage" # name で指定
u -i <instance> uitree text ref_5_12 # ref ID で指定
操作
u -i <instance> uitree click -p "PanelSettings" -n "BtnContinue" # name で指定
u -i <instance> uitree click ref_5_48 # ref ID で指定
u -i <instance> uitree click ref_5_48 --count 2 # ダブルクリック
u -i <instance> uitree click ref_5_48 --button 1 # 右クリック
u -i <instance> uitree scroll -p "PanelSettings" -n "ScrollArea" --y 100
モンキーテスト
u -i <instance> uitree monkey -p "PanelSettings" -c "action-btn" --count 50 --seed 42
u -i <instance> uitree monkey -p "PanelSettings" --duration 30 --seed 42
u -i <instance> uitree monkey -p "PanelSettings" -c "action-btn" --stop-on-error --json
query でフィルタした要素をランダムに click し、コンソールエラーを監視する。
-c (USS class) か -t (type) のフィルタが必須(フィルタなしでは要素が見つからない)。
事前に u -i <instance> uitree query -p <panel> -c <class> で有効な class 名を確認してから monkey に渡す。
--seed で操作順を再現可能。
スナップショット
u -i <instance> uitree snapshot save -p "PanelSettings" --name baseline # 現在のツリーを保存
u -i <instance> uitree snapshot diff -p "PanelSettings" --name baseline # baseline との差分
u -i <instance> uitree snapshot list # 保存済み一覧
u -i <instance> uitree snapshot delete --name baseline # 削除
diff 出力: 要素の追加(+)/削除(-)/classes 変化(~) を検出。
スクリーンショット
u -i <instance> screenshot # GameView (-s game がデフォルト)
u -i <instance> screenshot -p "ui-check.png" # 保存先指定
u -i <instance> screenshot --burst -n 5 # 連続 (アニメーション確認)
UI Toolkit / uGUI (Screen Space) は -s game でのみ映る。-s camera では映らない。
GameView のキャプチャにはエディタのフォーカスが必要。タイムアウトする場合は再試行で解決することが多い。
uGUI の場合
uitree は UI Toolkit 専用。uGUI は scene/component コマンドで操作する。
u -i <instance> scene hierarchy --depth 3
u -i <instance> component list -t "Canvas"
u -i <instance> component inspect -t "MyButton" -T "UnityEngine.UI.Button"
開発→テストフロー
Phase 1: 作成 & 手動確認
コード変更 → /unity-verify → play → dump → 操作 → screenshot → stop
Phase 2: pytest E2E テスト生成
dump で構造把握 → pytest テストファイルを生成 → 実行して確認
Phase 3: PlayMode テスト移植
安定したシナリオを C# に書き直す → CI で回帰テスト
Phase 1: 作成 & 手動確認
u -i <instance> play
u -i <instance> uitree dump -p "PanelSettings" # 構造把握
u -i <instance> uitree click -p "PanelSettings" -n "BtnStart" # 操作
u -i <instance> uitree text -p "PanelSettings" -n "StatusLabel" # 状態確認
u -i <instance> screenshot # 結果キャプチャ
u -i <instance> stop
Phase 2: pytest E2E テスト生成
Phase 1 で確認した操作シナリオを、pytest テストとして永続化する。
unity-cli の Python API (UITreeAPI, ConsoleAPI, EditorAPI) を直接使う。
手順:
u -i <instance> play+u -i <instance> uitree dumpで構造を把握する- 操作可能な要素(ボタン名、ラベル名)を特定する
- 以下のテンプレートに従い、
tests/integration/にテストファイルを生成する uv run python -m pytest tests/integration/<file> -vで実行して確認する
テンプレート:
"""E2E tests for <UI名> — pytest + unity-cli API."""
import time
import pytest
from unity_cli.api import ConsoleAPI, EditorAPI, UITreeAPI
from unity_cli.client import RelayConnection
PANEL = "<パネル名>" # u uitree dump で確認したパネル名
@pytest.fixture(scope="module")
def conn() -> RelayConnection:
conn = RelayConnection(instance="<プロジェクト名>", timeout=5.0)
try:
EditorAPI(conn).get_state()
except Exception:
pytest.skip("Relay not available")
return conn
@pytest.fixture(scope="module")
def uitree(conn: RelayConnection) -> UITreeAPI:
return UITreeAPI(conn)
@pytest.fixture(scope="module")
def console(conn: RelayConnection) -> ConsoleAPI:
return ConsoleAPI(conn)
@pytest.fixture(scope="module")
def editor(conn: RelayConnection) -> EditorAPI:
return EditorAPI(conn)
@pytest.fixture(autouse=True)
def _play_mode(editor: EditorAPI):
"""Play Mode に入る。"""
state = editor.get_state()
if not state.get("isPlaying"):
editor.play()
deadline = time.time() + 10
while time.time() < deadline:
if editor.get_state().get("isPlaying"):
break
time.sleep(0.5)
yield
@pytest.fixture(scope="module", autouse=True)
def _stop_after_all(editor: EditorAPI):
"""全テスト後に Play Mode 終了。"""
yield
try:
editor.stop()
except Exception:
pass
# --- Functional テスト ---
class TestMenuButtons:
def test_continue_shows_toast(self, uitree: UITreeAPI) -> None:
uitree.click(panel=PANEL, name="<ボタン名>")
time.sleep(0.3)
result = uitree.text(panel=PANEL, name="<ラベル名>")
assert result["text"] == "<期待値>"
# --- Smoke テスト ---
class TestSmoke:
BUTTONS = ["<ボタン1>", "<ボタン2>", ...]
def test_all_clickable_without_errors(self, uitree: UITreeAPI, console: ConsoleAPI) -> None:
console.clear()
for btn in self.BUTTONS:
uitree.click(panel=PANEL, name=btn)
time.sleep(0.2)
errors = console.get(types=["error"])
assert errors.get("entries", []) == []
# --- Structural Snapshot ---
class TestStructure:
def test_tab_switch_changes_tree(self, uitree: UITreeAPI) -> None:
before = uitree.dump(panel=PANEL)
uitree.click(panel=PANEL, name="<タブ名>")
time.sleep(0.3)
after = uitree.dump(panel=PANEL)
assert before != after
既存の conftest.py に conn, uitree, editor, console fixture がある場合はテンプレートの fixture 定義を省略し、conftest に委譲する。fixture の scope は conftest に合わせる(conftest が session なら _play_mode 等も同じ scope か互換性のある scope にする)。
動的な値(タイムスタンプ、プログレス%等)を持つラベルは等値比較ではなくフォーマット検証を使う:
assert result["text"].endswith("%") # "65%" 等
テストの種別に応じてクラスを追加する:
TestMenuButtons— Functional (click → text 検証)TestSmoke— 全ボタンクリック + エラーなしTestStructure— ツリー構造の変化検出TestMonkey— ランダム操作でエラーなしTestVisual— screenshot 撮影(画像比較は手動 or 別ツール)
Monkey テスト例:
from unity_cli.api.uitree_monkey import MonkeyRunner
class TestMonkey:
def test_random_clicks_no_errors(self, uitree: UITreeAPI, console: ConsoleAPI) -> None:
# MonkeyRunner.run() は MonkeyResult を返す。errors は list[dict]
runner = MonkeyRunner(uitree, console)
result = runner.run(panel=PANEL, class_filter="<操作対象class>", count=50, seed=42, interval=0.2)
assert result.errors == [], f"Monkey errors: {result.errors}"
Snapshot テスト例:
from unity_cli.api.uitree_snapshot import SnapshotStore
class TestSnapshot:
def test_save_and_diff_no_changes(self, uitree: UITreeAPI, tmp_path) -> None:
store = SnapshotStore(snapshot_dir=tmp_path / "snapshots")
data = uitree.dump(panel=PANEL, format="json")
store.save("baseline", data)
current = uitree.dump(panel=PANEL, format="json")
# diff(name, current): name は保存済みスナップショット名、current は比較対象のツリーデータ
result = store.diff("baseline", current)
assert result["added"] == []
assert result["removed"] == []
assert result["changed"] == []
monkey テストの前にUIを初期状態に戻す(前のテストの副作用で Toast 等のクラスが変わることがある)。time.sleep(2) で Toast の hidden 復帰を待つか、テスト順序に依存しない assertion を使う。
Phase 3: PlayMode テスト移植
Phase 2 の pytest で安定したシナリオを C# PlayMode テストに書き直す。
PlayMode に移植する基準:
- CI で毎回回す回帰テスト
- フレーム精度が必要 (アニメーション完了待ち等)
- Assert で厳密に状態検証
- InputSystem / EventSystem 経由の操作
pytest E2E のまま残す基準:
- 探索的テスト (シナリオが毎回変わる)
- AI エージェントの自律テスト
- Unity 外からの結合テスト
手順:
- Phase 2 の pytest テストから安定したシナリオを選ぶ
- 以下のテンプレートに従い、
Assets/Tests/PlayMode/に C# テストを生成する - asmdef に UI コントローラの参照を追加する(必要に応じて)
u -i <instance> tests run playで実行して確認する
テンプレート:
using System.Collections;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using UnityEngine.UIElements;
namespace Game.Tests.PlayMode
{
[TestFixture]
public class <UI名>Tests
{
private GameObject _hudObject;
private UIDocument _uiDocument;
private VisualElement _root;
[UnitySetUp]
public IEnumerator SetUp()
{
// UIDocument を持つ GameObject を生成
// コントローラが Awake/OnEnable で初期化するのを待つ
_hudObject = new GameObject("TestHUD");
_hudObject.AddComponent<<コントローラ名>>();
_uiDocument = _hudObject.GetComponent<UIDocument>();
// コルーチン初期化 (StartCoroutine in OnEnable 等) は1フレームでは完了しない
// 2フレーム以上待つ
yield return null;
yield return null;
_root = _uiDocument.rootVisualElement;
Assert.IsNotNull(_root, "Root visual element should be initialized");
}
[TearDown]
public void TearDown()
{
Object.Destroy(_hudObject);
}
// --- Functional テスト ---
[UnityTest]
public IEnumerator <ボタン名>_Shows<期待される動作>()
{
var btn = _root.Q("<ボタン名>");
Assert.IsNotNull(btn);
Click(btn);
yield return null;
var label = _root.Q<Label>("<ラベル名>");
Assert.AreEqual("<期待値>", label.text);
}
// --- Smoke テスト ---
[UnityTest]
public IEnumerator AllButtons_ClickableWithoutErrors()
{
var buttons = new[] { "<ボタン1>", "<ボタン2>", ... };
foreach (var name in buttons)
{
var element = _root.Q(name);
Assert.IsNotNull(element, $"{name} should exist");
Click(element);
yield return null;
}
LogAssert.NoUnexpectedReceived();
}
// --- ヘルパー ---
private static void Click(VisualElement element)
{
using var evt = ClickEvent.GetPooled();
evt.target = element;
element.panel.visualTree.SendEvent(evt);
}
}
}
Click ヘルパーの注意:
ClickEvent.GetPooled()で座標計算不要のイベントを生成し、panel.visualTree.SendEventで発火するNavigationSubmitEventではRegisterCallback<ClickEvent>が反応しないため使わない- ホバーやプレスエフェクトも再現したい場合は
PointerDownEvent+PointerUpEventをworldBound.center座標付きで送る
asmdef の注意:
- テスト対象クラスが
Assembly-CSharp(asmdef なし)にある場合、テスト asmdef からは直接参照できない - 対象クラスに専用 asmdef を作成し、テスト asmdef の
referencesに追加する - 例:
Assets/UI/Game.UI.asmdefを作成 →Game.Tests.PlayMode.asmdefの references に"Game.UI"を追加
Phase 2 の pytest テストの各テストメソッドを1対1で C# に移植する。
移植後は u -i <instance> tests run play で実行。
CLI 非対応操作
unity-shared のフォールバック順に従う:
u api schema --type <Type>で対応メソッドを検索u api callで実行- 該当なしの場合のみ YAML 直接編集