playcal について
playcal は、Unity Editor でゲームを開発する AI エージェント向けのツールキットです。編集、エディターの安定待ち、変更確認、差分確認を組み合わせ、ゲームイベント分析やログ要約、プレイヤーデータに基づくボット結果の補正も行います。現在は pre-alpha です。
主な機能
- 01Unity Editor の準備待ちと変更確認
- 02シーン差分の確認と結果のアサート
- 03Unity ログのエラー要約
- 04信頼区間付きレベル通過率の分析
- 05プレイヤーデータによるボットテスト補正
活用シーン
- 01エディターへの変更が反映されたか確認
- 02大量の階層データからオブジェクトを抽出
- 03Unity ビルドログの最初のエラーを特定
- 04レベルごとのプレイヤー通過率を推定
MCP 対応
`playcal mcp` は stdio MCP サーバーを起動し、各モジュールをツールとして公開します。ツール引数の定義は CLI と共通です。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
15掲載時のツール一覧 · 1 日前に更新
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| dir | string | いいえ | ベースラインの保存場所(デフォルト:.playcal/baselines) |
| name | string | はい | ベースライン名(例:level4-android) |
| floor | array | いいえ | この絶対値未満の低下を無視します(例:frame_ms=0.5)。繰り返し指定できます |
| action | enum | はい | save NAME FILE:実行結果をベースラインとして保存します。check NAME FILE:新しい実行結果と比較します。show NAMEsave | check | show |
| metric | array | いいえ | これらの指標のみを使用します(デフォルト:両方ともすべて) |
| source | string | いいえ | サンプル JSON ファイル。標準入力の場合は -(デフォルト) |
| tolerance | number | いいえ | 許容する低下率(%)(デフォルト:10) |
| percentile | number | いいえ | 比較に使うパーセンタイル(デフォルト:95) |
詳細
playcal
ゲームを作る AI エージェントのための、オープンソースツールキットです。 エージェントが信頼して使えるループを提供します(編集 → エディターが本当に落ち着くまで待つ → 変更が実際に反映されたことを確認する → 変更部分だけを見る → アサート)。また、ボットのプレイテストを実際のプレイヤーと照らし合わせて検証できます。「ボットはレベル 12 を簡単だと感じるが、人間がクリアする確率は約 41%(CI 30~53%)です。」
まずは Unity に対応しており、Unity 公式 CLI を基盤に構築されています。Godot と Unreal も、後から同じコアに接続されます。
AI エージェントの方は、まず llms.txt をお読みください。
ステータス:プレアルファ(v0.1.0)。 現時点では、イベントスキーマ、
ingest、report、calibrate、logs、loop、query、snap、observe、replay、baseline(パフォーマンスとメモリ)、visual、tests、affected、lock、checkpoint、doctor、MCP サーバー、Unity SDK が動作します。loop、query、snap、observe、replay、SDK のフレームレコーダーは、稼働中の Unity 6000.3 Editor で確認済みです。以下で planned と記されているものは、まだ実装されていません。長く見せかけたリストよりも、短く正直なリストを掲載したいと考えています。進捗はロードマップで追跡しています。

稼働中の Unity 6000.3 Editor での実際の実行例です。表示されているのは playcal の出力で、待機時間は短縮されています(実際の長さも表示されています)。
理由
ゲームエディターで作業するエージェントは、ありふれた、繰り返し発生する問題に遭遇します。
- コンパイルやドメインリロードが終わる前にツールを実行し、古い状態をもとに判断してしまいます。
- エディターにフォーカスがないため、再生中と表示されていても Play Mode がフレーム 1 のまま止まります。
- モーダルダイアログがすべてをブロックしていても、誰も気づきません。
- プレハブが保存されていないのに、「完了しました」と報告します。
- コンソールを一度読み取るだけ、またはヒエラルキーをダンプするだけで、10k 以上のトークンを消費します。
Unity 公式 CLI とコミュニティの MCP サーバーは、再生、テスト、キャプチャ、コンソールといった構成要素を提供します。しかし、こうした問題に対処するループはありません。playcal はそのループに加え、エディターの再起動後も保持されるシーン差分、パフォーマンスとスクリーンショットのゲート、フレークテストの隔離、人間のプレイヤーを基準にしたキャリブレーションを提供します。稼働中のエディターでこのループができること、できないことについては、ベンチマークをご覧ください。
インストール
Python 3.10+ が必要です。
pipx install playcal
playcal modules
最新の main を使うには、pipx install git+https://github.com/RizgarOzan/playcal を実行してください。Unity SDK は Git URL からインストールします。詳しくはUnity SDK(現在利用可能)をご覧ください。
AI エージェントを使ったインストール
Claude Code、Cursor、Codex、またはシェルを使える任意のエージェントに、次の内容を貼り付けてください。
Install playcal for this project:
1. Make sure pipx exists (python -m pip install --user pipx && python -m pipx ensurepath).
2. Run: pipx install playcal
3. Run: playcal modules --json and show me the module list.
4. Read https://github.com/RizgarOzan/playcal#usage and tell me which commands fit this project.
Do not change project files yet.
MCP サーバーとスキル
playcal mcp は stdio MCP サーバーです。各モジュールはツール(playcal_ingest、playcal_logs、playcal_loop など)になり、CLI と同じ引数定義から構築されるため、両者の仕様が食い違うことはありません。
サーバーの initialize 応答には、MCP の instructions として「どのコマンドをいつ使うか」の簡潔なガイドが含まれています。これはスキルファイルの冒頭と同じ内容で、エージェントは最初に doctor を実行し、エディターへの書き込みには loop を使うべきだと分かります。
# or pick it from the MCP Registry as io.github.RizgarOzan/playcal
claude mcp add --transport stdio playcal -- playcal mcp
code --add-mcp '{"name":"playcal","type":"stdio","command":"playcal","args":["mcp"]}'
エージェントがシェルを使える場合は、MCP よりスキルファイルの方が低コストです(すべてのプロンプトにツール一覧を含める必要がありません)。
mkdir -p .claude/skills/playcal && playcal skill > .claude/skills/playcal/SKILL.md
使い方
どのコマンドも --json を出力できるため、エージェントはテキストをスクレイピングせずに結果を読み取れます。
playcal ingest(現在利用可能)
このコマンドは、ゲームプレイイベントをschema/event-v0.jsonに照らして検証します。JSONL ファイル、またはPC0:ブロブを含む自由形式のテキストを受け付けます。ゲーム内の「結果を送信」ボタンでこれらのブロブが生成され、プレイヤーは任意のチャットに貼り付けられます。
playcal ingest sessions.jsonl --json
playcal ingest whatsapp-export.txt --paste
有効なイベントはaccepted.jsonlに保存されます。失敗した行はすべて、理由とともにrejected.jsonlに保存されます。
スキーマでは未知のフィールドは受け付けないため、名前、メールアドレス、デバイス ID、位置情報、IP アドレスが混入することはありません。PRIVACY.mdを参照してください。
playcal report(現在利用可能)
レベルごとのクリア率を、プレイヤー単位と試行単位の2通りで集計します。各率には95%のウィルソン区間が付くため、プレイヤーが10人の場合、単に「60%」と示してもほとんど意味がないことが分かります。
playcal report accepted.jsonl
playcal report accepted.jsonl --json
異なるgame_versionは別々に集計され、レベルは自然な順序(1、2、10)で並びます。
playcal calibrate(現在利用可能)
ボットはすべてのビルドのすべてのレベルをプレイできますが、人間がどのようにプレイするかも把握したいものです。calibrateは、ボットのプレイ結果と、一部のレベルをプレイした少数の人間パネルの結果を受け取り、ボットのクリア率と人間のクリア率の対応関係を学習します。そして、ボットがプレイしたすべてのレベルについて、人間のクリア率を95%区間とともに予測します。
playcal calibrate --human panel.jsonl --bot bots.jsonl
# version level bot predicted human human seen
# 1 03 50% 35% [21%-52%] 36% n=25 *
# 1 06 67% 50% [33%-66%] -
# leave-one-out: 5/5 human rates inside their interval, median error 4 points.
どちらのファイルも同じイベントスキーマを使用します。レベルごとのランダム効果回帰で、人間のロジットをボットのロジットに当てはめます。そのため、ボットが独自のやり方で失敗するレベルがあると、回帰直線をひそかに引っ張るのではなく、すべての区間が広がります。少なくとも3つのレベルで人間のデータが必要です(各レベル5回以上の試行、--min-attempts)。4つ以上のレベルがある場合は、各レベルを順番に1つずつ除外し、実際のクリア率が予測区間に収まるかどうかも確認します。この結果から、残りの予測をどれほど信頼できるかが分かります。レベルが少なければ区間は広くなりますが、それが正直な答えです。
playcal logs(現在利用可能)
Unity のEditor.log(または Player.log、コンソールから貼り付けたテキスト)を、エージェントに必要な情報へとまとめます。同じエラーが1000回繰り返されてもcount: 1000の1項目になり、後続のエラーは通常、最初のエラーから派生したものなので、最初の原因は個別に取り出されます。
playcal logs --json # this machine's Editor.log
playcal logs Editor.log --since "Reloading assemblies"
cat Player.log | playcal logs -
adb logcat -d | playcal logs - # Android: only the Unity tag is read
終了コード1は、エラーが見つかったことを示します。CIでは、バッチモードのビルドログも同じ方法で読み取るため、後続エラーの山ではなく、最初の原因でジョブが失敗します。
- run: unity -batchmode -quit -projectPath . -executeMethod Build.Run -logFile build.log || true
- run: pipx run playcal logs build.log --json
playcal loop(実行中の Editor で動作)
loop settle は、Editor が本当に準備完了になるまで待機します。ドメインリロード中に状態が一時的に変わることがあるため、「ready」のポーリングが連続して 2 回成功するのを確認します。Unity がコンパイル中またはリロード中は、unity status には Editor がまったく表示されないため、settle は unity pipeline list に理由を問い合わせます。Safe Mode、ダイアログで停止している Unity(editor_blocked)、または本当に Editor が存在しない状態のいずれかが返されます。Editor が 2 つ開いている場合も、両方の処理がタイムアウトするのではなく、そのことが報告されます。
loop run は Editor が準備完了かを確認し、unity command を 1 つ実行してから、もう一度確認します。また、実行した処理を検証することもできます。各確認では editor status を 1 回呼び出し、Editor がコンパイル中またはリロード中かどうかを明示します。settle のポーリングは、ビジー状態または応答のない Editor、およびリロードを開始する書き込みに対するフォールバックです。
playcal loop run "gameobject create" --verify "scene get_hierarchy" --expect "result.roots.*.name=Probe" --diff --json -- --name Probe
--verify は書き込み後に読み取りコマンドを実行し、--expect はその JSON に対してアサーションを行います(path=value、path!=value、path~text、または「存在する」を表す単独の path。* は任意の項目に一致します)。--diff は読み取り前後で変化した内容を表示します。ダイジェストには、Editor または CLI の問題は fault: "infra"、コマンド自体の誤りは fault: "task" と記録されるため、エージェントは再試行すべきか、考え直すべきかを判断できます。成功と報告された書き込みが反映されていない場合は、verify_failed(終了コード 5)で失敗します。
実行中の Editor で遭遇した 2 つの注意点(Unity 6000.3、Pipeline 0.10.0-exp.1):
- コンパイルエラーがある Editor でも、「ready」と表示されます。
--compileはunity recompileも実行し、file、line、code、messageを含むエラーとともにcompile_error(終了コード 6)で失敗します。 - 一部のコマンドは、戻り値を返してから数秒後にリロードを開始します(
package resolveはrequiresRecompileを返します)。loop はこれを検知し、再び「ready」を信頼する前にリロードの開始を待ちます。スクリプトをディスクに書き込む場合など、ほかの処理にも同じ待機を強制するには--reload-wait SECONDSを指定します。
この Editor に対して、読み取り、書き込み、--expect の動作を確認しました。Safe Mode の分岐は、まだ実環境では確認できていません。
playcal query(現在動作)
エージェントが必要としているのが 3 つの名前だけでも、階層全体をダンプすると数万トークンになることがあります。query は指定した情報だけを残し、どれだけ削減したかを表示します。
playcal query --unity "scene get_hierarchy" --from "result.roots.**" --where "name~Enemy" --select name --select activeSelf --json
playcal query scene.json --from data.objects --where "tag=Enemy" --select name
--where は loop --expect と同じ構文を使用します。* はリスト内の任意の項目、** は任意の深さにあるすべてのものを表します。
playcal checkpoint(現在動作)
エージェントのステップ前にプロジェクトのファイルを保存し、保存した状態をいつでも復元できます。保存データはプライベート参照(refs/playcal/checkpoints)上の git コミットとして記録されるため、ブランチ、HEAD、ステージング領域は変更されず、プッシュも行われません。.gitignore は尊重されます。
playcal checkpoint save "before level 4 tweak"
playcal checkpoint list
playcal checkpoint diff latest
playcal checkpoint restore ~1 # one save back; prints how to undo the restore
復元時に書き換えるのは差分のあるファイルだけなので、Unity がすべてを再インポートすることはありません。loop run --checkpoint を指定すると、すべての書き込みの前に保存が行われ、ダイジェストが失敗した場合は undo コマンドが含まれます。Unity はシーンとプレハブを保存したときにのみディスクへ書き込むため、保存されていない Editor の変更はチェックポイントに含まれません。
playcal snap(現在動作)
読み取り結果に名前を付けて保存し、後から何が変わったかを確認できます。そのため、「この実行でシーンに何が起きたか」を、2 つのダンプではなく数行で確認できます。
playcal snap save before --unity "scene get_hierarchy"
# ... the agent works, or you play a round ...
playcal snap diff before --unity "scene get_hierarchy" # exit 1 when something changed
playcal snap diff run-a run-b --json
Unity の instanceIds は 1 つの Editor セッション内でしか有効ではありません。そのため、再起動後やプレイモード移行後に単純な差分を取ると、すべてが削除されて再追加されたと表示されます。snap は instanceId を除外し、hierarchyPath、次に path、guid、name の順でオブジェクトを照合します。同じ名前の兄弟オブジェクトは Enemy と Enemy#2 になります。isDirty も編集のたびに切り替わるため除外されます。Unity の読み取り結果に限らず、どの JSON ファイルでも使用できます。保存先は .playcal/snaps/ です。
実行中の Editor でシーンを保存してオブジェクトを追加したところ、差分には added: result.roots[hierarchyPath=/SnapProbe] と表示されました。その後、保存して Unity を再起動してから再度差分を取りましたが、変更はありませんでした。すべての instanceId が新しくなり、Unity があるオブジェクトの古い ID を別のオブジェクトに割り当てていたにもかかわらず、結果は同じでした。
playcal observe(実行中の Editor で動作)
Play を押してゲームを数秒間実行し、実行中に記録されたコンソールエラー(logs と同様に、まとめて最初の原因を表示)、シーン内で変化した内容(snap のルールに従います)、再生中のシーンに対する --expect チェックの結果を、ひとつの要約として報告します。赤判定の実行でのみスクリーンショットを撮影します。
playcal observe --seconds 5 --expect "result.roots.*.name=Player"
# errors (task)
# LogError: boss has no target at Assets/Boss.cs:13
# scene: 1 added, 0 removed, 0 changed
# played: 5.0-19.4 s before the second read
# shot: C:\MyGame\.playcal\observe\observe.png
終了コード 0 は問題なし、1 は赤判定(コンソールエラー、チェック失敗、読み取り失敗、Play Mode が開始されなかった場合)、3 または 4 は Editor 自体に関するエラーです(loop と同じコードです)。理由が console_unread の終了コード 3 は、コンソールを読み取れなかったか、実行中にクリアまたはオーバーフローしたことを意味するため、エラーを見逃した可能性があります。そのため、observe は問題なしとは判定しません。コンソールのマークは Play の開始前に取得されるため、それ以前のエラーは対象外です。実行中のゲームではシーンが変化するのが想定されているため、シーンの差分は報告のみです。差分で判定するには --expect を使ってください。--read CMD は scene get_hierarchy の代わりに別の読み取り結果を比較します。--no-play は Play Mode に触れずに --seconds 秒間隔で 2 回読み取った結果を比較します。すでに再生中または一時停止中の Editor は、状態を変えずに監視します。observe が開始した Play Mode は、実行が途中で失敗した場合も必ず停止します。played は 2 回目の読み取りまでゲームを実行した時間です。最初の数値以上(Editor が再生中だと通知した時点から計測)、2 番目の数値以下(開始リクエスト時点から計測)になります。
実行中の Editor(Unity 6000.3、Pipeline 0.10.0-exp.1)で、問題のない observe --seconds 3 の実行には約 33 秒かかります。時間がかかる原因はリロードではありません。editor.playmode start は、完了するまで戻ってこないためです。時間がかかるのは CLI 自体で、unity の呼び出しは内容にかかわらず毎回 3~5 秒かかります。そのため observe は、以前の 16 回に対して 7 回の呼び出しで済みます。Play 前後の読み取りは、それぞれ 1 回の job batch 呼び出しで行い、数秒間 Editor が表示されないこともある unity status のポーリングではなく、すぐに「playing」と返す editor status を使います。最初のバージョンでは約 70 秒かかり、3 秒を指定してもゲームが約 26 秒間実行されていました。その後、存在しない prefab からオブジェクトを生成するスクリプトを追加すると、observe はファイル名と行番号付きで ArgumentException を報告し、シーン内の「1 added」と実際の Game ビューのスクリーンショットを 45 秒で記録しました。まだ実環境で確認できていないのは、「Enter Play Mode Options」でリロードをスキップする設定のプロジェクトと、実行中にコンソールがオーバーフローするケースです(オーバーフロー時は要約に dropped と表示されます)。
playcal replay(実行中の Editor で動作します)
セッションの入力を一度記録してから、変更を加えるたびに再生し、ゲームが引き続き同じ動作をするか確認できます。ゲームは SDK 経由で入力を読み取り、同じ結果になるべき値にマークを付けます。
float move = PlaycalInput.Axis("move.x", Input.GetAxis("Horizontal")); // live value, or the recorded one during a replay
if (PlaycalInput.Button("jump", Input.GetButton("Jump"))) Jump();
PlaycalReplay.Mark("score", score); // compared frame by frame
playcal replay record jump-test --frames 600 # press Play, record 600 frames, leave Play
playcal replay run jump-test # replay with the same frame rate and seed, compare the marks
# diverged
# frame 150: expected x=0.181579992 at 150, got x=0.150087655 at 150
playcal replay run jump-test --stop-at 120 # pause on frame 120 and stay in Play Mode
playcal replay step --frames 5 # advance the paused Editor 5 frames
playcal replay check jump-test # compare the last result again, e.g. with --tolerance
Playcal は Play の開始時に .playcal/replays/request からリクエストを取得するため、シーンを変更する必要はありません。記録中または再生中は、記録時の fps に合わせて Time.captureFramerate を設定するため、毎フレームの deltaTime が同じになります。また、UnityEngine.Random のシードも設定します(自分で作成した System.Random には影響しません)。終了コード 0 は一致、1 は不一致、途中で終了したリプレイ、実行中のコンソールエラー、または no_marks(マークがないため、比較対象がない状態)を示します。3 は observe と同様に console_unread を示します。no_sdk はリクエストが一度も読み取られなかったことを意味します。整数のマークは完全に一致する必要があり、--tolerance で許容範囲が広がるのは小数のマークだけです。記録されるのはフレームなので、入力は FixedUpdate ではなく Update で読み取ってください。
実行中の Editor(Unity 6000.3、Pipeline 0.10.0-exp.1)で、入力が壁時計から取得されるプローブスクリプトを使って 300 フレームを記録しました。そのため、その入力を再現できるのは記録だけでした。マークしたのは、移動した値、Random.value、およびボックス上を転がる Rigidbody のボールの位置です。--tolerance 0 を指定しても、物理演算を含め、2 回のリプレイはいずれも記録と完全に一致しました。フレーム 150 で入力を 1 つ変更すると、フレーム 150 で diverged と表示されました。--stop-at 120 を指定すると、リプレイのフレーム 120 で Editor が一時停止し、step --frames 5 を実行すると 125 に進みました。observe と同じ変更を加えた場合、120 フレームの記録には約 42 秒、実行には約 37 秒かかり、その大半は各 unity 呼び出しにかかる 3~5 秒でした。まだ確認していないのは、キーボードを使った人のプレイ、Editor の再起動後または別のマシンでのリプレイ、プレイヤービルド、多数のボディやジョイントがあるシーン、および新しい Input System です(値は同じように PlaycalInput 経由で渡してください)。deltaTime が固定されるため、処理が追いつかないマシンではフレームが落ちるのではなく、記録がスローモーションで実行されます。
playcal baseline(JSON で今すぐ使用できます)
パフォーマンステストの実行結果をベースラインとして保持し、後続の実行がノイズを超えて遅くなった場合にのみ失敗させます。フレームごとのサンプルを {"frame_ms": [...], "gc_alloc_bytes": [...]} またはフレーム時間のリストだけで渡します。
playcal baseline save level4 run-main.json
playcal baseline check level4 run-branch.json # exit 1 only when slower
playcal baseline check level4 run.json --percentile 99 --tolerance 5 --floor frame_ms=0.5
デフォルトでは p95 を比較し、その差をブートストラップ法で評価します。95% 区間の下限でさえ許容値(デフォルトは 10%)を超えた場合にのみチェックが失敗します。数回のヒッチがある短い実行は inconclusive となるため、ノイズの多い CI マシン 1 台のせいでビルドが失敗することはありません。--floor を指定すると、絶対値で見て影響が小さすぎる遅延を無視できます。ベースラインは .playcal/baselines/ に保存されます。Unity SDK の PlaycalFrames.Begin("level4") がサンプルを記録し、Save() が baseline で読み取れる場所に保存します。used_memory_bytes の系列にも同じチェックが適用されるため、より多くのメモリが必要な実行は同じように失敗します。1 MB 未満の増加を無視するには、--floor used_memory_bytes=1048576 を追加してください。ビジュアルゲートについては、下記の playcal visual を参照してください。
playcal tests(現在利用できます)
テスト結果の履歴を保持し、不安定な失敗と本当の失敗を区別します。Unity の -testResults が出力する NUnit3 XML と JUnit XML を読み込みます。
playcal tests check TestResults.xml # exit 1 only on failures of tests that aren't flaky
playcal tests flaky # tests that passed and failed on the same commit
同じコミットで一度でも成功し、失敗もしたテストは、不安定と判定されます。そのため、初回の再実行で成功すると不安定とマークされます。その後の失敗は、実行を失敗扱いにする代わりに quarantined として表示されます。忘れられないよう、引き続き一覧には表示されます。check はまず履歴に照らして実行結果を判定し、その後、結果を履歴に追加します(--no-record を指定すると追加されません)。履歴は .playcal/tests/history.jsonl に保存されます。CI のキャッシュに保持するか、コミットしてください。不安定なテストは、修正して履歴から該当行を削除するまで、後続のコミットでも隔離されたままになります。
affected は変更によって影響を受ける可能性のあるテストアセンブリを特定するため、小さな変更のたびにスイート全体が終わるのを待つ必要がありません。
playcal tests affected # uncommitted work; --base main for a branch
# -runTests -assemblyNames "Game.UI.Tests"
プロジェクトの .asmdef ファイルと .asmref ファイル(名前と GUID: 参照)を読み込み、変更された各スクリプトを、その上位にある最も近い asmdef のアセンブリに割り当ててから、参照をたどってテストアセンブリを特定します。asmdef がないスクリプトは、Assembly-CSharp または Assembly-CSharp-Editor に割り当てられます。シーン、プレハブ、設定、Packages/manifest.json の変更はコードをたどって特定できないため、その旨を伝えて全テストの実行を促します。プロジェクト外から file: で参照されているパッケージはスキャンされません。
playcal visual(現在、PNG ファイルで利用できます)
スクリーンショットのゲートです。スクリーンショットをベースラインとして保存し、後で撮影したものと比較します。
playcal visual save level4-start shot.png
playcal visual check level4-start shot.png # exit 1 if more than 0.1% of pixels changed
# changed: 4096 pixels (0.1975%) differ, box [900, 300, 963, 363], see .playcal/visual/level4-start.diff.png
チャンネルの値が --tolerance(デフォルトでは 255 段階中 8)を超えて変化したピクセルは、変更ありと判定されます。そのため、アンチエイリアスや圧縮によるノイズは許容されますが、スプライトの欠落は検出されます。--max-changed は、変更が許容されるピクセルの割合をパーセントで指定します。失敗時には差分 PNG を出力します。変更されたピクセルは、新しいスクリーンショットの淡色表示の上に赤色で示され、ボックスでエージェントに確認箇所を伝えます。標準ライブラリのみを使って、インターレースなしの 8 ビットおよび 16 ビット PNG(グレースケール、RGB、RGBA)を読み込みます。16 ビットのスクリーンショットは 8 ビットで比較されます(各サンプルの上位バイトを使用)。1920x1080 のスクリーンショットの処理には約 2 秒かかります。アニメーションを一時停止して解像度を固定した状態でスクリーンショットを撮影してください。そうしないと、ゲートがアニメーションを計測してしまいます。時計や FPS カウンターのように自動で変化する部分は、--ignore x0,y0,x1,y1 で除外できます(複数のボックスを指定する場合は繰り返し指定します)。
playcal lock(現在利用できます)
2 つのエージェント、またはエージェントと人が同じプロジェクトで作業する場合、両者が Level4.unity を編集しないようにする必要があります。lock は .playcal/locks/ 内にロックファイルを作成します。
playcal lock take Assets/Scenes/Level4.unity --owner agent-a --ttl 30m # exit 1 and who holds it, if taken
playcal lock check Assets/Scenes/Level4.unity --owner agent-b
playcal lock release Assets/Scenes/Level4.unity --owner agent-a
playcal lock list
複数のパスを指定した場合、すべて成功するか、すべて失敗するかのどちらかです。ロックには有効期限があるため、クラッシュしたエージェントがシーンを永久にロックすることはありません。また、自分のロックを再取得すると有効期限が更新されます。PLAYCAL_OWNER を設定すると、--owner の入力を省略できます。各エージェントが書き込み前にロックを要求する場合にのみ機能します。Unity や git がファイルに書き込むのを防ぐものではありません。Git Bash を使う場合、先頭に / のない Unity パスを渡すか、MSYS_NO_PATHCONV=1 を設定してください。Git Bash は /SnapProbe を Windows パスに書き換えるためです。
playcal doctor(現在利用できます)
何かが起動しないときは、まず実行してください。Python、git、Unity CLI、CLI から認識できる各 Editor(実行中、ダイアログで停止中、Safe Mode)、さらに --project を指定した場合は SDK が Packages/manifest.json に含まれているかを確認します。チェックが 1 つでも失敗すると終了コード 1 を返します。--json を指定すると、チェック結果をリストとして出力します。
playcal doctor --project C:/Games/MyGame
# ok unity cli: 1.0.0-beta.12
# fail editor C:\Games\MyGame: running but not answering; usually a modal dialog is open in the Editor
Unity SDK(本日から利用可能)
Git URL https://github.com/RizgarOzan/playcal.git?path=/unity/com.playcal.sdk からインストールし、PlaycalLog.Init()、log.LevelStart("04")、log.LevelFail(...) を呼び出します。「結果を送信」ボタンは PlaycalLog.CopyResult() を呼び出します。使い方はパッケージの READMEに記載されています。C# の出力が Python 側と同じスキーマに準拠していることを確認するテストもあります。
パフォーマンス計測では、PlaycalFrames.Begin("level4") によってフレーム時間、フレームごとの GC 割り当てバイト数、使用メモリの合計(ProfilerRecorder 経由)を記録し、最初の 30 フレームは除外します。最後に Save() を呼び出し、そのファイルを playcal baseline に渡します。Editor で計測した GC 値には Editor 自体の割り当て(テストシーンでは約 15 KB/フレーム)が含まれるため、ベースラインはプレイヤービルドから取得してください。
playcal replay では、PlaycalInput.Axis / PlaycalInput.Button で入力を読み取り、PlaycalReplay.Mark で結果を記録します。詳細はリプレイのセクションを参照してください。
ビジュアルゲートでは、PlaycalShots.Capture("level4-start") によってスクリーンショットを persistentDataPath/playcal/shots/ に保存します。バッチモードのテスト実行では画面キャプチャが動作しないため、代わりに Camera.main をレンダリングします。
ロードマップ
| モジュール | 内容 | 状態 |
|---|---|---|
ingest | イベントスキーマ、検証、ペースト用データ | ✅ 動作 |
report | レベルごとのクリア率、試行回数、Wilson 信頼区間 | ✅ 動作 |
| Unity SDK | レベルイベント、PC0「結果を送信」行、データ削除、18 歳未満向け機能の無効化 | ✅ 動作 |
loop | 編集 → 安定化(コンパイル/再読み込み、フォーカス、モーダル)→ 書き込みの検証 → 差分 → アサーション、単一の JSON ダイジェスト | ✅ 読み取り、書き込み、--expect を Unity 6000.3 Editor 上で確認済み。Safe Mode 分岐は実環境で未確認 |
calibrate / predict | ボットの指標+小規模な人間パネル → 信頼区間付きの人間の予測クリア率 | ✅ 試行ごとのクリア率では動作。実際のパネルでの確認は未実施 |
query | 任意の JSON 結果(ファイル、stdin、Unity の読み取りコマンド)からフィールドを選択、フィルター、射影 | ✅ JSON および実行中の scene get_hierarchy の読み取りで動作 |
checkpoint | エージェントの各ステップを Git ベースの取り消し可能なチェックポイントとして保存 | ✅ 動作(loop run --checkpoint が各書き込みの前に保存) |
logs | コンソール/Editor.log の要約:重複排除、最初の原因、エラー種別 | ✅ 動作 |
mcp、skill | モジュールレジストリから生成される MCP サーバーと SKILL.md | ✅ 動作 |
snap | 読み取り結果を保存し、セッション ID を除外して 2 つの保存結果を比較 | ✅ 動作。Editor の再起動をまたいで確認済み |
observe | シーン全体のランタイム差分、赤判定時のみスクリーンショットを撮影 | ✅ Unity 6000.3 Editor 上で動作確認済み(エラー、シーン差分、スクリーンショット) |
baseline | コミットごとのビジュアル、パフォーマンス(フレーム時間 p95、GC)、メモリのゲート | ✅ パフォーマンスとメモリのゲート、SDK レコーダー、playcal visual としてのビジュアルゲート |
replay | フレームステップ実行、記録した入力、リプレイ → 回帰テスト | ✅ Unity 6000.3 Editor 上で動作確認済み(記録、マッチ、乖離、停止、ステップ実行)。人がプレイする場合や再起動をまたぐ場合は未確認 |
tests | 不安定な履歴と隔離、影響を受けるテスト | ✅ 履歴、不安定テストの検出、隔離(NUnit3、JUnit)、.asmdef グラフからの影響を受けるテストアセンブリ |
lock | 複数エージェント向けのシーン/アセットロック | ✅ 動作(有効期限付きのアドバイザリーロックファイル) |
doctor | 実行前にセットアップを確認 | ✅ 動作 |
| Godot、Unreal | 同じコアで新しいバックエンド | 後日 |
各行は 1 つのモジュールを表します。モジュールは playcal.modules グループに属する Python エントリーポイントで、任意の Editor 用 C#、独自のテスト、独自のベンチマークシナリオを持ちます。新機能は、インストールする新しいツールではなく、新しいモジュールとして追加されます。
ベンチマーク
bench/live.py は、実際に起動している Editor に対して、4 つの状態で 1 回ずつ書き込みを行うテストを、2 つの方法で実行します。1 つは通常の unity command を送信して success を信頼する単純なエージェント、もう 1 つは読み戻しチェック付きの playcal loop run です。「書き込み成功」は、どちらかの報告を信頼するのではなく、その後 Editor から読み戻して判定します。Unity 6000.3.9f1、Pipeline 0.10.0-exp.1、Windows 11、3 ラウンドで実施しました。
| Editor の状態 | 単純方式で反映 | playcal で反映 | 誤った成功報告(いずれか) | 単純方式/playcal の所要時間の中央値 |
|---|---|---|---|---|
| アイドル | 3/3 | 3/3 | 0 | 2.2 s / 7.4 s |
| ドメインリロード中 | 0/3 | 3/3 | 0 | 2.2 s / 33.6 s(19~41 s。リロードそのもの) |
| Play Mode 中(書き込み拒否) | 0/3 | 0/3 | 0 | 3.9 s / 28.0 s |
| 不正な値(文字列を float に入力) | 0/3 | 0/3 | 0 | 4.3 s / 5.4 s |
結果を簡潔にまとめると、次のとおりです。
- Unity のリロード中に送信した書き込みは通常の CLI では破棄されますが、playcal では反映されます。6 回の実行すべてで、実際にリロード中に書き込みを試みました。
- Unity の CLI は予想以上に厳格でした。Play Mode 中の書き込みと不正な値を明確に拒否したため、どちらの方式も誤った成功を報告しませんでした。playcal のダイジェストには失敗の種類(どちらも
fault: task)も追加されるため、エージェントは単純に再試行してはいけないと判断できます。 - playcal は、静かな Editor に対する単純な呼び出しと比べると、依然として約 3 倍遅くなります。
unityの呼び出しにはそれぞれ約 3 秒かかり、その一部はstatusで CLI が使用状況分析を送信する時間です。準備完了の確認ではeditor statusを 1 回呼び出し、読み戻しも同じバッチで行うため、静かな状態での書き込みは、当初の 6 回から 3 回に減りました。アイドル時の所要時間は 24 秒から 15 秒、さらに 7 秒になりました(#4)。リロード中の所要時間は、ほとんどがリロードそのものです。この実行では Pipeline サーバーが停止するまでに 10~24 秒かかりましたが、より負荷の低い実行では 14 秒でした。 - 拒否されたケースと不正な値のケースでは、Editor が拒否を返すまで時間がかかります。Play Mode では書き込みが約 25 秒保留された後に拒否され、不正な値は 5 秒で返されました。
- いくつかのオブジェクト名の読み取りでは、完全な
scene get_hierarchyが 5216 バイト、playcal queryが 682 バイトでした。テスト用シーンは小さいため、シーンが大きくなるほど差も広がります。
まだ測定していない項目は、3 つ目の方式としての MCP サーバー、タスク全体でのエージェントのトークン使用量、実際の Editor でのフォーカスがない状態とモーダルダイアログのケースです。bench/sim.py は、これらのケースに加えて、書き込みは反映されるものの Play 開始時にゲームが一度例外を発生させるケースを、CI でスクリプト化した Editor に対して再現します(loop だけでは成功と判定しますが、observe なら検出します)。これは回帰を防ぐためのものであり、Unity に関する証拠ではありません。
ご自身のプロジェクトで実行できます(BenchLive* オブジェクトと一時スクリプトを 1 つ作成して削除します)。
python bench/live.py --project-path C:/path/to/Project --repeat 3
エージェントを使って人間の難易度を予測するという発想は、新しいものではありません。Roohi ら(2021)は、多くのプレイヤーを抱える Rovio のゲームでこれを行いました。playcal が新たに提供するのは、小規模なパネル(友人 10~30 人)を使い、信頼できる信頼区間とオープンなコードで実施することです。
コントリビューション
最も役立つのは、エージェントと Unity Editor の同期がずれた実例です。エージェントが実行した内容、報告した内容、実際の状態をお知らせください。そのための Issue テンプレートがあります。次に歓迎するのは、新しいモジュールと、Godot または Unreal の API に詳しい方によるバックエンドです。
プルリクエストを作成する時間がなくても、ご自身のプロジェクトで 4 つのコマンドを実行し、出力を テスター募集 に投稿していただければ、私が自分でテストをもう 1 つ書くよりも参考になります。
CONTRIBUTING.md に、セットアップ方法(pip install -e ".[dev]" と pytest。ほとんどのテストで Unity は不要です)と、いくつかの基本ルールを記載しています。行動規範もお読みください。セキュリティ上の問題は公開 Issue ではなく、SECURITY.md に従ってご報告ください。
説明が明確な小規模タスクは、good first issue にあります。playcal によってデバッグの時間を節約できた場合は、スターを付けていただけると、ほかの Unity ユーザーにも見つけてもらいやすくなります。
ライセンス
Apache-2.0。商用ゲームでご利用いただけます。特許ライセンス条項により、スタジオが許可を求める必要はありません。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。