GoKart:オートパイロットが午後のうちに作り上げたマリオカート風レースゲーム
Agent! のオートパイロット(Auto-Pilot)に「GoKart という名前のマリオカートクローンを作れ」というゴールをひとつ渡して戻ってくると、3 つのコース、8 種類のアイテム、AI ライバル、そして 3,344 件のテストチェックに合格する Godot 4 のレースゲームができていました。行き詰まった場面も含めて、ログが語る実際の経緯をまとめます。
昨日の記事ではオートパイロット(Auto-Pilot)を紹介しました。Mac 版 Agent! に /auto <ゴール> と入力すると、Stop All を押すまでそのゴールに向かって無人でサイクルを回し続けます。この記事は、それをゲームに向けたときに反対側から何が出てきたかの話です。
ゴールは、私が打ち込んだほぼそのままです:
create a Mario Kart clone called GoKart with all Mario Kart effects. I believe Godot 4 can do the Mario Kart effects, but I haven't built any of them yet: drift sparks and boost flames (GPUParticles3D), speed lines and boost blur (screen-space shaders, glow and tonemapping), item effects and tire trails (shaders plus ribbon meshes), kart movement (VehicleBody3D or custom arcade physics). Write unit tests and test the game frequently.
予算:時間制限なし、サイクル数無制限。すべてのサイクルで Agent! 内のモデルは Claude Sonnet 5.5 でした。最初のコミットは 12:39。同じ日の 16:32 には、リポジトリのコミット数は 33 になっていました。現在は GDScript ファイルが 47 個、GDScript とシェーダーのコードが約 5,500 行、そして 3,344 件のチェックに合格するユニットテストスイートがあります。すべて GitHub の AgentiLoop/GoKart にあります。
オートパイロットがやること、1 サイクルずつ
オートパイロットはプロジェクト内の .agent/autopilot/progress.md に継続的なログを残し、各サイクルが「何をしたか、何を前提にしたか、何が残っているか、何かに阻まれたか」を追記していきます。以下の内容はすべてそのログが出典です。自分の記憶ではなくログを引用するのは、ログのほうが私より正直だからです。
サイクル 1 の時点でマシンに Godot はありませんでした。brew install --cask godot を実行し、バイナリにシンボリックリンクを張り、git init を行い、kart_physics.gd をシーン依存のない純粋なモデルとして書きました。加速、ブレーキ、後退、摩擦、ステアリング、開始した方向に固定されるドリフト、そして 3 段階のミニターボ。VehicleBody3D ではなく独自のアーケード物理をあえて選び、理由も述べています。「マリオカート風の挙動をユニットテストしやすくするため」。テスト 11 件、チェック 17 件、最初のコミット。
サイクル 2 は、私が名指しで頼んだエフェクトを追加しました。ミニターボ段階に応じて色が変わる GPUParticles3D のドリフト火花、排気の炎、リボンメッシュのタイヤ痕、そして放射状ブーストブラー、スピードライン、ビネットのためのスクリーンスペースシェーダー。チェック 43 件。
サイクル 3 はコースを作りました。3 メートルごとにリサンプリングした Catmull-Rom ループ、路面リボン、コライダー付きの赤白ストライプの壁、スクロールするシェブロンシェーダー付きのブーストパッド、順序付きの 8 つのチェックポイント、そしてショートカットや逆走周回をカウントしないラップトラッカー。新しいテストのひとつは、実際の物理モデルで 3 周を走り切る追従ボットです。タイムは 1:02.5。チェック 90 件。
サイクル 4 は、虹色フレネルシェーダーのアイテムボックス、ルーレット、そして最初の 3 つのアイテムを追加しました。キノコ、バナナ、壁で跳ね返るミドリこうら。被弾するとカートは 1.2 秒間スピンします。チェック 235 件。
そして、行き詰まりました。
行き詰まった場面
次のサイクルは AI カートとヘッドレスのスモークレースを追加し始め、そのまま戻ってきませんでした。次のセッションで判明した原因は、main.gd の 115 行目に紛れ込んだタブ 1 文字。パースエラーのせいでスモークスクリプトがエラーを吐き続け、それを動かしていたシェルコマンドには時間制限がありませんでした。
私は Stop All を押し、同じゴールに大文字のメモを添えて新しいセッションを開始しました。全文は載せませんが、要旨はこうです。スモーク実行のテストで行き詰まった、行き詰まるな、シェルに時間制限をかけろ。
2 回目のセッションの最初のサイクルはタブを修正し、AI が内側の壁に向かってコーナーをショートカットしないように AI の先読みを路面サンプル 10 個から 6 個に減らし、コーナー速度リミッターを追加し、macOS には timeout コマンドがないため、すべてのシェル実行を perl -e 'alarm 100; exec @ARGV' で包みました。それ以降、ほぼすべてのサイクルの末尾でログには「すべての実行は perl alarm の制限下で行った」と書かれています。ゴールの文面から教訓を学び、それを守り続けたのです。
そのセッションは約 1 時間 15 分で 15 サイクルを回し、どのサイクルも 1 つの機能です。タイミングよくアクセルを踏むとロケットスタートのブーストがつくスタートカウントダウン、ミニターボ段階アップのポップと画面端のフラッシュ、ミニマップ、追尾するアカこうらとスター、回転するホイール・ステアリングする前輪・頭を向けるドライバーを備えた手続き的なカートモデル、ライバル全員を縮めるサンダー、カートの周りを回るトリプルこうら、路面に沿って先頭を追い詰めるトゲゾーこうら、ポイント付きのリザルト画面、エンジンのループからゴールのジングルまで音声ファイルを一切使わない完全合成オーディオ、2 つ目のコースを備えたタイトルメニュー、周回数オプション、3 つ目のコース、そして各 AI カートの位置付き 3D エンジン音。
正直だった場面
ログの中で私がいちばん気に入っているのは、告白が繰り返されるパターンです。Agent! は PNG を見ることができず、サイクルごとにそう言いました:
ウィンドウモードのスクリーンショット実行ではエラーは記録されませんでしたが、私は画像を見ることができないので、コースや HUD がどう描画されているかは確認していません。
サウンドは聞いていませんし、スモークレースもスクリーンショットツールも実行していません。
私のツールでは画像を見られないので、そのレビューには人が必要です。
そこで、できることをテストしました。実際のシーンをインスタンス化して状態をアサートするヘッドレスのシーンチェックです。サンダーを発動し、ライバル 3 台がスケール 0.5 でスピンしていること、そして雷が後で片付けられることを確認。密集したスタートグリッドにトゲゾーこうらを撃ち、12 フレーム目の爆発とカート 2 台のスピンを確認。プレイヤーを強制的にゴールさせ、オートパイロットがカートを AiDriver に引き渡し、リザルトパネルに「1st YOU」と表示されることを確認。
3 回目のセッションも同じように止まりました。今度は寛大すぎる 240 秒の alarm の裏で止まり、1 サイクルで私が停止しました。そして人が見ました。その人は私で、4 回目のセッションのゴールは、私のプレイテストメモを軽く整えたものです:
the UI needs to scale with the window. the UI should not be prone to the screen speed effects and blurry. Should be able to use cursor keys. it's not clear when power ups are released. the tops of the walls flicker. Little too hard to steer the kart. hard to keep up with the computer AI karts. maybe have AI difficulty levels Easy Medium and Hard.
1 サイクル後:UI がウィンドウに合わせてスケールする canvas-items ストレッチ、HUD をスピードエフェクトのオーバーレイより上のキャンバスレイヤーへ移動、矢印キーとアイテム用の Enter / Ctrl と画面上のヒント、イーズインするステアリング、壁のストライプの z-fighting 修正(隣り合う赤と白のセグメントが同じピクセルを奪い合っていたので、白のほうをほんの少し大きくしました)、4x MSAA、そして AI の最高速度を 0.72、0.85、1.0 に調整する Easy / Medium / Hard。テストは 2,156 件から 3,344 件のチェックに増えました。スイートの読み込みを妨げていた tests/test_items.gd の既存のパースエラーも見つけて修正したことが理由のひとつです。
自分で止まった場面
その最後のセッションのサイクル 2 から 8 は、コードを一切変更しませんでした。それぞれがリポジトリを読み直し、スイートを再実行し、同じ段落の言い換えを書きました:
画面上で結果を確認できなかったので、ゴール達成とは宣言しません。4 つの項目は、まだ人がゲーム内で試す必要があります。
これが正しい振る舞いです。ゴールは「ステアリングの感触が悪い」「壁がちらつく」であり、どんなヘッドレステストもそのゴールを閉じることはできません。オートパイロットには反復回数の上限がないので、永遠に確認を続けたでしょう。セッションはサイクル 8 の後に終了し、GoKart に次に必要だったのはもう 1 サイクルではなく、プレイテストでした。
これらのスクリーンショットについて
撮ったのは Agent! で、私ではなく、手動でもありません。ランダム走行のスクリーンショットを 3 枚頼んだところ、70 行の tools/random_drive.gd を書きました。ふらつくレーンオフセットで路面をたどり、ランダムにドリフトを挟み、持っているアイテムをランダムなタイミングで撃ち、数百物理フレームごとに 1 フレームを保存します。コースごとに 1 回ずつ実行し、上の 3 枚はそれぞれから 1 フレームを選んだものです。GoKart の README にも掲載しています。
試す前に伝えておきたいこと
- git の中で動かすこと。 オートパイロットに取り消しはありません。GoKart のログが読めるのは、すべてのサイクルがコミットで終わっていたからで、サイクルが一度脱線したときも何も失われませんでした。
- 運用ルールはゴールに書くこと。 「シェルに時間制限をかけろ」は、単発のメッセージよりもゴールの一部としてのほうが効きました。新しいサイクルは毎回ゴールを読み直すからです。
- 目を求められることを想定すること。 見た目や手触りに関することなら、ループは嘘をつかずに正直に止まります。セッションの間にプレイテストの時間を確保し、メモを次のゴールとして戻してください。
- Stop All はワークフローの一部であって、失敗ではありません。GoKart の最初の 3 セッションはすべてそれで終わりました。
GoKart 0.0.1
試すのに Godot はもう必要ありません。GoKart 0.0.1 は同じリポジトリから書き出した最初のパッケージ版リリースです:
- macOS ユニバーサル(Apple シリコンと Intel)、Developer ID で署名し Apple による公証済み
- Windows x86_64
- Linux x86_64 と arm64
各ダウンロードはゲームデータを埋め込んだ単一の自己完結型バイナリで、受け取ったものを確認したい場合はリリースページに SHA256SUMS.txt があります。Windows 版は未署名なので、SmartScreen の警告が出ます。
オートパイロットを搭載した Agent! 1.1.87 はリリースページと Homebrew で入手できます。ソースから GoKart を実行したい場合は Godot 4.4 以降が必要です:git clone https://github.com/AgentiLoop/GoKart.git && cd GoKart && godot --path .