🦁

UnityEditorをCLIで操作するツールを作った

に公開

概要

Unity Editorをターミナルから操作するためのツールを作ったので紹介します。

https://github.com/bigdra50/unity-cli

なぜ作ったか

Unity開発でコーディングエージェントを使う上で、 UnityMCPのようなMCPツールでUnityを操作できるようにするのが一般的かと思います。
コンソールをコピペすることもなくなり、 非常に便利で実際に開発で利用していました。
ただ、 自分の使い方では以下の課題もありました。

  1. MCPは常駐型の設計
  • コーディングエージェントが常にツールにアクセスできる反面、 コンテキストウィンドウを常時消費(実測18.4kトークン)
  • 実験していた当時のスクラップはこちら
  1. 開発者自身もエディタをCLIから操作したい
  • コーディングエージェント主体の開発スタイルになるほど、ターミナルであらゆる作業を完結させたくなる
  • エラーログを読ませて分析させるとき、 「エラーログを読んで」と指示するところから始めるより !unity console get のように直接コマンドを実行してコンソールのログをコンテキストに渡すところから始めた方が確実

そこで、 UnityMCPの基本的な仕組みを参考にしつつCLI用途に対応させる方針で作ることにしました。

インストール

Unity

Unity Package Managerから以下のURLでインストールできます。

https://github.com/bigdra50/unity-cli.git?path=UnityBridge

またはOpenUPMを使う場合

$ openupm add com.bigdra50.unity-bridge

CLI

uvxで直接実行

# プレイモード制御
$ uvx --from git+https://github.com/bigdra50/unity-cli u play
$ uvx --from git+https://github.com/bigdra50/unity-cli u stop

# コンソールログ取得
$ uvx --from git+https://github.com/bigdra50/unity-cli u console get -l E

グローバルインストール

$ uv tool install git+https://github.com/bigdra50/unity-cli

インストール後はunity-cli, u, unityの3つのエイリアスが使えます

$ unity-cli state
$ unity state
$ u state

シェル補完も利用できます

# zsh
$ u completion -s zsh > ~/.zsh/completions/_unity-cli

# bash
$ u completion -s bash >> ~/.bashrc

# fish
$ u completion -s fish > ~/.config/fish/completions/unity-cli.fish

シェル補完

使い方

unity-cliのコマンドは大きく2つに分かれます。

UnityEditor起動不要のコマンド

プロジェクト情報の取得

$ u project info
╭──────────────────────────────────────────────────╮
│ MyGame                                           │
╰────── /Users/username/Projects/MyGame ───────────╯
Company: MyCompany
Version: 1.0.0
Unity: 2022.3.20f1

Unityバージョンだけ取得したい場合:

$ u project version
Unity: 2022.3.20f1

インストール済みUnityエディタの管理

$ u editor list
                 Installed Editors (2)
┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Version      ┃ Path                            ┃
┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╩
│ 2023.2.10f1  │ /Applications/Unity/Hub/Editor/… │
│ 2022.3.20f1  │ /Applications/Unity/Hub/Editor/… │
└──────────────┴──────────────────────────────────┘

プロジェクトを開く

git clone後などよく使います。

# Unity Editorでプロジェクトを開く
$ u open

# 特定バージョンで開く
$ u open --version 2022.3.20f1

設定の確認

$ u config show
=== Unity CLI Configuration ===
Config file: Not found (using defaults)
Relay host: 127.0.0.1
Relay port: 6500
Timeout: 15.0s
Instance: (default)

UnityEditor起動が必要なコマンド

Unity Editorが起動した状態でRelay Serverを介して操作するコマンドです。
以下のコマンドを使う前に、Relay Serverの起動と接続が必要です。

Relay Serverの起動

Unity Editorで Window > Unity Bridge を開き、Start ServerConnect の順にクリックします。

Unity Bridge ウィンドウ

StatusがConnectedになれば準備完了です。

接続状態はエディタ右上のツールバーバッジからも確認できます。バッジをクリックすると接続・切断の切り替えも可能です。

ツールバーバッジ

CLIから直接Relay Serverを起動することもできます。

$ uvx --from git+https://github.com/bigdra50/unity-cli unity-relay --port 6500

Relay Serverは1つのプロセスで複数のUnityインスタンスからの接続を受け付けます。
複数のUnityプロジェクトを同時に開いている場合でも、各インスタンスから同じサーバーにConnectするだけでよく、サーバーを2重に起動する必要はありません。

操作対象のインスタンスは u instances で確認し、-i オプションで指定できます。

# 接続中のインスタンス一覧
$ u instances
               Connected Instances (1)
┏━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓
┃ # ┃ Project     ┃ Unity Version ┃ Status ┃ Default ┃
┡━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩
│ 1 │ TestProject │ 6000.2.6f2    │ ready  │    *    │
└───┴─────────────┴───────────────┴────────┴─────────┘
# インスタンスを指定してコマンド実行
$ u -i TestProject state

C#スクリプトのコンパイル確認

スクリプト編集後、コンパイルエラーがないか確認する流れ:

# AssetDatabaseをリフレッシュしてコンパイル実行
$ u refresh
[OK] Asset database refreshed

# エラーがあれば表示される
$ u console get -l E
06:53:37 error Assets/Scripts/Player/PlayerHealth.cs(44,13): error CS0201: Only
assignment, call, increment, decrement, await, and new object expressions can be
used as a statement
06:53:37 error Assets/Scripts/Player/PlayerHealth.cs(44,13): error CS0103: The
name 'hoge' does not exist in the current context

# エラー修正後、コンソールをクリアしてから再コンパイル
$ u console clear
[OK] Console cleared

$ u refresh
[OK] Asset database refreshed

# エラーなし = 出力なし
$ u console get -l E

-v オプションでスタックトレース付き表示も可能です。ランタイムエラーの調査に使えます:

$ u console get -l E -v

テストの実行

EditModeテスト:

# テスト一覧を確認
$ u tests list edit
{
  "mode": "edit",
  "count": 38,
  "tests": [
    { "fullName": "Game.Tests.Editor.DamageCalculatorTests.Calculate_AppliesCriticalMultiplier_WhenCritical", ... },
    { "fullName": "Game.Tests.Editor.InventoryTests.AddItem_IncreasesSlotCount_WhenNewItem", ... },
    ...
  ]
}

# テスト実行
$ u tests run edit
Running tests (46/46) Pass:38 Fail:0 Skip:0

PlayModeテスト:

$ u tests list play
$ u tests run play
Running tests (17/17) Pass:19 Fail:0 Skip:0

menu execはMenuItemをCLIから実行するコマンドです。 既存のMenuItemの実行にも使えますが、使い捨てスクリプトを書かせて実行させるのも便利です。

unity-cliの各コマンドではカバーしきれないEditorAPI操作を頼みたいとき、エージェントに[MenuItem]付きのスクリプトを書かせてmenu execで実行させます。
例えば、 シーンへのオブジェクト配置やインスペクター操作が必要な作業などがあります。

# 1. エージェントが [MenuItem("Tools/TempSetup")] 付きスクリプトを生成
# 2. コンパイル→実行→後片付け
$ u refresh
$ u menu exec "Tools/TempSetup"
$ rm Assets/Editor/TempSetup.cs && u refresh

EditorAPIで可能なことなら何でもこの方法で実行できるので、CLIに専用コマンドがない操作の受け皿になります。

コーディングエージェントとの連携

エージェント向けのエラーメッセージ

コーディングエージェントが自律的にコマンドを実行していく上で、エラー発生時にリカバリしやすいようエラーメッセージには次のアクションを含めるようにしています。

# Playモードでないのにpauseしようとした場合
$ u pause
Error: Cannot pause: not in play mode. Enter play mode first with action 'enter'.

# 不正なアクションを渡した場合
$ u play --action foo
Error: Unknown action: foo. Valid actions: enter, exit, pause, unpause, step, state

# 接続先インスタンスが見つからない場合
$ u state
Error: Instance not found. Check available instances with 'u instances'.

エージェントはエラーメッセージを読んで次に実行すべきコマンドを判断できるため、人間の介入なしにリカバリを進められます。

連携例: UIの作成からスクリーンショットまで

以下はClaude Codeに「UI Toolkitでニューモーフィズム風のゲームタイトル画面を作って、シーンに配置して、スクリーンショットを撮って」と指示した際の実行ログです。UXML/USS/C#の作成からUnity上での動作確認まで、すべてClaude Codeが自律的に行っています。

GameViewのスクリーンショット
UIToolKitで用意したデモ用のUI

# 1. UXML, USS, C#スクリプトをエージェントが作成した後...

# AssetDatabaseをリフレッシュしてコンパイル
$ u refresh
[OK] Asset database refreshed

# コンパイルエラーがないか確認
$ u console get -l E
(出力なし = エラーなし)

# シーンにGameObjectを作成してコンポーネントを追加
$ u gameobject create -n SampleHUD
$ u component add -t SampleHUD -T UnityEngine.UIElements.UIDocument
$ u component add -t SampleHUD -T SampleHUDController
$ u scene save

# PlayModeに入ってUIの表示を確認
$ u play
[OK] Entered play mode

# UIツリーをダンプして構造を確認
$ u uitree dump -p "PanelSettings" -d 3
Panel: PanelSettings (36 elements)

VisualElement "Root" .root ref_3
  VisualElement "StatusBar" .status-bar ref_4
    Label "StatusTime" ref_5
  VisualElement "TitleCard" .neu-card .profile-card ref_6
    VisualElement .title-area ref_7
    VisualElement .xp-section ref_10
  VisualElement "Chapters" .section ref_22
    Label .section-title ref_23
    VisualElement .card-row ref_24
  VisualElement "Menu" .section ref_46
    VisualElement .action-row ref_47
  VisualElement "Toast" .toast .hidden ref_54
    Label "ToastMessage" ref_55
  VisualElement "TabBar" .neu-card .tab-bar ref_57
    VisualElement "TabHome" .tab .tab-active ref_58
    VisualElement "TabQuest" .tab ref_60
    VisualElement "TabCodex" .tab ref_62
    VisualElement "TabConfig" .tab ref_64

# GameViewのスクリーンショットを撮影
$ u screenshot -s game -p ./screenshot.png
[OK] Screenshot captured: ./screenshot.png

$ u stop
[OK] Exited play mode

連携例: UIツリーの探索とインタラクション

uitreeコマンドはPlaywright MCPに似たref IDベースの操作体系で、UI Toolkitの要素を検索・検査・操作できます。

以下はClaude Codeに次のプロンプトを渡した際のセッションの例です。
Claude Codeがdump→query→text→click→inspectのサイクルを自律的に回しています。

Playモードに入って、unity-cliのuitreeコマンドでUI Toolkit要素を探索してください。
UIツリーの構造把握、要素の検索・検査・テキスト取得、クリック操作による状態変化の検証まで一通り試してください。

https://x.com/bigdra50/status/2021266197296071040?s=20

# Playモードに入ってパネル一覧を取得
$ u uitree dump
Panels:
  [Editor] DockArea (21 elements)
  [Editor] Toolbar (76 elements)
  [Player] PanelSettings (65 elements)
  [Player] UIDocument SamplePanelSettings (SampleHUD) (64 elements)
  ...

# UIツリーの構造をダンプ (ref IDが各要素に割り振られる)
$ u uitree dump -p "SampleHUD" -d 3
Panel: UIDocument SamplePanelSettings (SampleHUD) (20 elements)
UIDocumentRootElement "SampleHUD-container" ref_1
  VisualElement "Root" .root ref_2
    VisualElement "StatusBar" .status-bar ref_3
    VisualElement "TitleCard" .neu-card .profile-card ref_4
      VisualElement .title-area ref_5
      VisualElement .xp-section ref_8
    VisualElement "Chapters" .section ref_15
      Label .section-title ref_16
      VisualElement .card-row ref_17
    VisualElement "Menu" .section ref_18
      VisualElement .action-row ref_19
    VisualElement "Toast" .toast .hidden ref_20
    VisualElement "TabBar" .neu-card .tab-bar ref_21

# ref IDでテキスト読み取り
$ u uitree text ref_167
ref_167 Label: Story Progress

$ u uitree text ref_197
ref_197 Label: SETTINGS

# 型で要素を検索
$ u uitree query -p "SampleHUD" -t Label
Found 29 elements in "SampleHUD":
  ref_147 Label "StatusTime" ...
  ref_148 Label .game-title ...
  ...

# 子要素付きで検査 → Chapter1がcard-selectedを持っていることを確認
$ u uitree inspect ref_171 --children
ref_171 VisualElement "Chapter1"
  classes: .neu-card .featured-card .card-selected
  visible: True
  ...

# Chapter2をクリックして選択状態の変化を検証
$ u uitree click ref_178
ref_178 VisualElement: Clicked element

$ u uitree inspect ref_178
ref_178 VisualElement "Chapter2"
  classes: .neu-card .featured-card .card-selected  # ← 選択が移動した
  ...

# タブをクリック → tab-activeの移動を確認
$ u uitree click ref_155
ref_155 VisualElement: Clicked element

$ u uitree inspect ref_155
ref_155 VisualElement "TabQuest"
  classes: .tab .tab-active    # ← tab-activeが付与された
  ...

# CONTINUEボタンクリック → Toastメッセージ確認
$ u uitree click ref_192
ref_192 VisualElement: Clicked element

$ u uitree text ref_153
ref_153 Label: Loading save data...

dump → click → inspect/text のサイクルで、Claude CodeがUI操作とその結果検証を自律的に回せます。

Claude Codeにコマンドを教える

MCPのように常時コンテキストを消費しない代わりに、Claude Codeにコマンド体系を知らせる手段が必要です。基本的にはプロジェクトのCLAUDE.mdによく使うコマンドを簡潔に書いておくだけで事足りています。

## Commands

| コマンド                         | 用途                      |
| -------------------------------- | ------------------------- |
| `u state`                        | エディタ状態の取得        |
| `u play` / `u stop`              | Play/Stopモード制御       |
| `u refresh`                      | AssetDatabaseリフレッシュ |
| `u console get -l E`             | エラーログの取得          |
| `u tests run edit`               | EditModeテスト実行        |
| `u uitree dump -p <panel>`       | UIツリーのダンプ          |
| `u screenshot -s game -p <path>` | スクリーンショット撮影    |

ただし、「refresh→コンパイルエラーチェック→テスト→ランタイム確認」のような一連のワークフローまでCLAUDE.mdに書き出すと、結局コンテキスト消費が増えて本末転倒です。
そこで、ワークフロー単位の手順はSkillとして切り出しています。
Skillは呼び出し時にのみコンテキストに読み込まれるため、常時消費を避けつつ複雑な手順をエージェントに渡せます。

これらのSkillは試験的にClaude Codeプラグイン化しています。

# マーケットプレイスから追加
/plugin marketplace add bigdra50/unity-cli

# インストール
/plugin install unity-cli@unity-tools

最後に

コーディングエージェント周辺のツールチェーンは変化が速く、今の設計が半年後も最適とは限りません。
実際に使いながら必要な機能を追加・改善していくつもりです。

Skillはプロジェクト固有のワークフローに依存する部分が多いため、当面はCLI本体の機能拡充を優先していく方針です。

Issue・PRは歓迎です。使ってみて気づいたことがあればぜひフィードバックをお待ちしています。


追記(2026-02-11): Relay Serverの起動手順・ツールバーバッジによる接続確認・複数インスタンス接続時の補足を追加しました。

Discussion