Claude Code や Codex にゲームのコードを書かせる場面が、この一年で目に見えて増えました。 リファクタ、システムの実装、シェーダーやゲームプレイの調整まで、エージェントが差分を出してくる速度は人間の比ではありません。 問題は、その差分が正しく動くかどうかではなく、速いままかどうかです。
AI がコードを書く時代のボトルネック
コードを書く手間が下がると、ボトルネックは「書く」から「確かめる」に移ります。 機能が動くかどうかはテストが自動で答えを出します。 けれど、そのコードが実機のフレームタイムを何ミリ秒か押し上げていないかは、テストの緑では分かりません。
エージェントが一日に何十件も差分を出すようになると、その一つひとつを人間が手でプロファイルするのは現実的でなくなります。 描画スレッドに無自覚なアロケーションが増える、更新ループの計算量がそっと悪化する、GC の走る頻度が上がる。 どれも単体テストは通り、レビューの目視でも見抜きにくく、実機に載せて初めて体感の劣化として現れます。
書く速度がエージェントで上がったぶん、確かめる工程が新しいボトルネックになります。
だから性能の確認を、レビュアーの気付きや職人的な勘に頼る工程のままにしておくわけにはいきません。 エージェントと同じ速度で回る、機械側の判定に置き換える必要があります。
回帰ゲートを機械側に置く
やることは単純です。 ビルドごとに性能テレメトリを集め、直前の既知の良好なビルドと比べて、しきい値を超えて悪化していたら CI を落とす。 人間が数字を眺めて判断するのではなく、回帰の有無を機械が判定して、パイプラインの合否として返します。
Framedash の framedash perf-diff は、二つのビルドのフレームタイム、メモリ、GPU 時間について P50 と P95 の両方を出すコマンドです。
--fail-on-regression を付けると、候補ビルドの P50 がしきい値を超えて悪化したときに終了コード 1 で落ちます(ゲートの判定は P50 の比較で行い、P95 は参考として併記されます)。
framedash perf-diff --baseline "$BASE_SHA" --candidate "$GITHUB_SHA" \
--threshold 5 --fail-on-regression
比較の対象はフレームタイム、メモリ、GPU 時間に加えて、マップのロード時間(load_time_ms)とディスク IO(io.*)です。
いずれも小さいほど良い指標として扱われます。
--metric で特定の指標だけにゲートを絞る、--map や --platform で比較範囲を限定するといった調整もできます。
ここで効いてくるのが、比較の単位となる build_id です。
どのビルドの数字なのかが一意に決まっていなければ、ビルド間の比較そのものが成り立ちません。
ワークフローの全体像
エージェントが書いたコードが、この回帰ゲートを通るまでの流れを追います。
ゲートが落ちたら、エージェント自身がテレメトリを読んで原因を絞り込みます。
CI 側でこの流れをまとめて回すのが framedash run-profile-test です。
このコマンドは、自動セッション用の FRAMEDASH_* 環境変数を書き出し、指定したプロファイリング用ビルドを起動し、そのテレメトリが取り込まれるのを待ってから、ベースラインに対して perf-diff のゲートをかけます。
framedash run-profile-test \
--command "./Build/Game.exe -nullrhi -ExecCmds='Automation RunTest Perf'" \
--scenario nightly --api-key-file ci-read.key \
--baseline "$BASE_SHA" --threshold 5 --fail-on-regression
起動されたゲーム側では、自動テストの入口で自動セッション API を一度呼びます。
SDK が BeginAutomatedSessionFromEnvironment() を呼ぶと、run-profile-test が書き出した FRAMEDASH_BUILD_ID / FRAMEDASH_GIT_BRANCH / FRAMEDASH_GIT_COMMIT / FRAMEDASH_TEST_SCENARIO を読み取り、以後のイベントすべてに CI ビルドと、そのブランチ、コミット、シナリオが自動で乗ります。
イベントごとにタグ付けするコードは要りません。
TelemetrySDK.Instance.BeginAutomatedSessionFromEnvironment();
// ... プロファイリングのシナリオを実行 ...
TelemetrySDK.Instance.EndAutomatedSession();
運用上の注意が一つあります。
短いビルドやヘッドレス実行は、SDK の定期フラッシュより先に終了することがあります。
テレメトリが実際に送信されるまで、実行を生かしておいてください(各 SDK の CI とヘッドレス実行の手順に方法があります)。
そのうえで run-profile-test が取り込みを待ってからゲートをかけます。
build_id はトップレベルのフィールドとして記録され、ブランチとコミットとシナリオは ci.branch / ci.commit / ci.scenario の属性に乗ります。
これで、エージェントが出したコミットの build_id が、そのまま回帰比較の候補になります。
鍵の使い分けだけ注意が要ります。
ゲートはテレメトリを analytics:read の鍵で読み、起動されたゲームは別の events:write の取り込み用の鍵でテレメトリを送ります。
名前の衝突を避けるため、ゲート側の鍵は FRAMEDASH_API_KEY 環境変数ではなく --api-key-file で渡し、FRAMEDASH_API_KEY はゲームのイングスト鍵として空けておきます。
ゲートが落ちたら、どこを見るか
perf-diff が落ちたとき分かるのは、「どの指標が、どれだけ悪化したか」までです。
どこで悪化したのかは、この数字だけでは分かりません。
そこで使うのがパフォーマンスヒートマップです。 FPS、フレームタイム、GPU 時間、メモリ使用量を、ゲームマップ上にセル単位でオーバーレイして見せます。 デバイスやビルドプロファイルで絞り込めるので、回帰したビルドのどのマップのどのあたりが重くなったのかを、マップ上の位置として特定できます。
マップ上の分布は、位置付きのイベントから描かれます。 プロファイリングのシナリオがプレイヤー位置を登録済みのマップ ID とともに送ると、SDK はそのイベントにカメラ向き(ヨーとピッチ)を自動で添えます。 これがあれば、「build 1042 の desert_ruins、北向きで見た一角で P95 が跳ねる」という粒度まで追えます。 回帰は、再現可能な調査対象になります。
エージェント自身にテレメトリを読ませる
ここまでの流れには、まだ人間が一箇所残っています。 ヒートマップを開いて原因を絞り、直すコードを書く工程です。
この工程も、エージェントに渡せます。 Framedash の MCP サーバーは、テレメトリに対する読み取り専用のツールとリソースを LLM に公開します。 ヒートマップのグリッドデータ、ダッシュボードの KPI、生の SQL クエリまで、エージェントが自然言語の指示から直接引けます。
claude mcp add framedash \
-e FRAMEDASH_API_KEY=fd_xxx \
-e FRAMEDASH_PROJECT_ID=your-project-uuid \
-- npx -y @framedash/mcp-server
これで、回帰を起こしたエージェント自身に、自分が壊した箇所を読ませて直させる一周が組めます。
get_heatmap でマップ上の重い箇所を俯瞰し、query を build_id で絞って候補ビルドのホットスポットを切り出し、原因の見当を付けてコードを直す。
ツールは読み取り専用なので、エージェントがテレメトリを書き換えたり消したりすることはありません。
直したコードが本当に回帰を解消したかどうかは、次のビルドで同じ CI ゲートがもう一度客観的に判定します。
判定は機械に、原因の読み解きはエージェントに。 人間が残るのは、しきい値をどこに引くかと、直し方の妥当性を最後に見る場所です。
始め方
必要なのは、ゲームに SDK を入れて build_id を CI から渡すことと、CI にゲートを一つ足すことだけです。
SDK の統合は数行で済み、対応エンジンは Unity、UE5、Godot (C#) です。
- SDK と CI の具体的な設定は CI 連携プロファイリングにまとまっています。
- コマンドの一覧は CLI リファレンスを参照してください。
- エージェントに使わせるなら、Claude Code プラグインが MCP サーバーとスキルを一括で入れます。
claude plugin marketplace add crane-valley/framedash-claude-plugin
claude plugin install framedash@framedash