2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIに塊魂を作らせたら、実在の東京14,563棟を呑み込むゲームになった ——150エージェントの制作ログ全解剖

2
Posted at

「AIに塊魂を作らせたら、東京まるごと巻き込むゲームになった」——これは比喩ではありません。ボールを転がしてネジや抵抗を取り込む2cmのスタートから、実在の東京・秋葉原の街を呑み込み、最後はスカイツリーごと地球から離陸する。そのゲームを、Claude(Fable 5)が ultracode というマルチエージェント・ワークフローで、設計からデプロイまで end-to-end に作り上げました。

🟢 この記事はAIと一緒に作成しました — 本文の執筆・技術ファクトチェック・図版整理を Claude(Fable 5)のマルチエージェントが行い、人間が監修・公開しました。掲載した数字はすべて作業ログの実測値で、AIが生成した内容を人間が検証した上でお届けしています。

本記事は「AIにゲームを作らせてみた」という軽い話ではなく、実際にどう作られたのかの技術的な記録です。バンドルサイズ、エージェント数、トークン消費、バグの原因と修正——数字はすべて作業ログ(docs/worklog/)の実測値で、盛っていません。AIが書いたものを正直に「AIが書いた」と認めた上で、その中身を解剖します。

塊魂へのリスペクト

このプロジェクトのインスパイア元は、バンダイナムコ(当時はナムコ)の名作 塊魂(かたまりだましい) です。生みの親である高橋慶太さんが世に問うたあの独創的なゲームに、まず深い敬意を表します。

塊魂を知らない方のために——プレイヤーは「塊(かたまり)」と呼ばれる粘着性のボールを転がし、身の回りの物を巻き込んでいきます。鉛筆や画鋲のような小さな物から始まり、巻き込むほどに塊が大きくなり、大きくなれば今度はもっと大きな物(机、自動車、家、ビル、最後は雲や島まで)を巻き込めるようになる。この「小さな物から始まって青天井に大きくなっていく」あの圧倒的な気持ちよさは、一度遊んだら忘れられません。

そして塊魂の真骨頂は、スケールが桁違いに変わっても、体験がシームレスに繋がり続けることにあります。2cmの塊と直径数百mの塊が、同じ操作・同じ気持ちよさで地続きに繋がっている。この「シームレスなスケールアップ」こそが、塊魂を唯一無二にしている高難度のコア体験です。本プロジェクトの技術的な主題は、まさにここ——桁違いのスケール変化をどう技術的に再現してシームレスに繋ぐかにあります。塊魂が成し遂げたあの感覚を、ブラウザの上で、AIのマルチエージェントが、どこまで作れるのか。それがこのデモの挑戦です。

本作は塊魂へのオマージュとして制作した、学習・技術検証目的の非商用デモです。「塊魂」の商標および著作権はバンダイナムコエンターテインメントに帰属します。本作はバンダイナムコ社とは一切関係のない、独立したファンメイドの技術デモであることを明記します。

Fable Katamari タイトル画面。秋葉原のパーツショップから始まる

完成物

操作はWASD/矢印キーで転がし、Spaceでダッシュ。目標は大きくなって東京スカイツリー(634m)を巻き込むこと。クリアタイムでS〜Dランクが付くタイムアタックです。

技術スタックは Three.js r177 + Vite 6 のみ。物理エンジンも使わず、全ジオメトリ・色・効果音(WebAudio合成)をコードで生成しています。コードで生成しない外部アセットは、v3 で導入したドナックのスプライト(8 WebP, 約20KB)と、v4 以降の東京地図バイナリ(約284KB gz)だけです。

なぜ作ったか — Fable 5 の性能デモ

このプロジェクトの目的は、Fable 5(Claude)がどこまで一貫した品質で複雑なソフトウェアを作れるかを検証することでした。単発のコード生成ではなく、

  1. ブラウザで動く3Dゲーム(塊魂風:取り込んで成長)
  2. 成長に応じて「一段階大きい視界」へシームレスに移行する仕組み
  3. 取り込みスケールが5桁変わっても重くならない設計
  4. ultracode(マルチエージェント)で実行
  5. 全作業の思考・意思決定をログに残す
  6. Cloudflare Pages にパブリック公開

という要件を、人間のプロダクトオーナーの追加注文に応えながら5バージョンにわたって育てていく。それがどこまで通用するか、という実験です。

結論を先に出すと、v1〜v4 だけで 延べ約142エージェント / サブエージェント消費 約1,718万トークン / 実働17時間超。v5(6エージェント・約97.7万トークン)を加えると累計 約148エージェント / 約1,816万(約18M)トークン規模になります。以下、その中身を見ていきます。

ultracode とは — 3つの「型」

ultracode は、Claude が自分自身を多数のサブエージェントに分割し、ワークフローツール(pipeline / parallel の合成、resumeFromRunId での中断再開対応)でオーケストレーションする方法です。今回のプロジェクトでは大きく3つの型を使い分けました。

ここから先、誰が何を言ったかを追えるように、エージェントを**色(絵文字)**でラベル付けします。

役割
🟦 設計エージェントA(パフォーマンス最優先レンズ)
🟩 設計エージェントB(ゲーム体験最優先レンズ)
🟨 設計エージェントC(シンプルさ最優先レンズ)
🟪 審査エージェント(ジャッジ/統合担当)
🟥 レビュー指摘エージェント
🟧 反証エージェント(懐疑者)

型1: judge panel(独立提案 → 採点審査 → 統合)

v1 の設計フェーズで採用。単一案のバイアスを避けるため、3つのエージェント(🟦🟩🟨)が互いの存在を知らずに独立して設計案を出します。各案は構造化スキーマ(scaleSystem / physics / rendering / spawning / fileLayout / interfaces / risks)を強制され、その上で審査エージェント🟪が採点し、最良案をベースに他案の長所を移植して、並列実装可能なファイル分割とモジュール間インターフェースまで確定させます。

🟦 提案A: 性能   🟩 提案B: 体験   🟨 提案C: シンプル   ← 互いに不可視・並列
        \            |            /
         \           |           /
              🟪 審査エージェント                       ← 採点 + 長所の移植
                     |
        [確定設計 + ファイル分割 + I/F]                  ← 並列実装の契約に

各エージェントが提案した核心

  • 🟦 設計エージェントA(パフォーマンス最優先): シミュレーションを常にボール半径0.5〜2.5という狭い数値帯で回し、ティア昇格時に1フレームで一様な相似変換 S=0.2 を全状態に適用する。SoA(構造体の配列をフラットな型付き配列に展開)、零アロケーション(ゼロ・パー・フレーム)。
  • 🟩 設計エージェントB(ゲーム体験最優先): 後述の**「シームレス法則」**——tierIndex は見た目(スポーン内容・パレット・演出)だけを駆動し、吸収判定・カメラ・フォグ・速度はすべて半径の連続関数にする。閾値でゲームプレイが分岐しない構造そのものを作る。
  • 🟨 設計エージェントC(シンプルさ最優先): 最小構成・最少パーツ・デバッグ容易性。ティアは座標ではなくコンテンツだけをゲートし、?r=5 等の開発キーでテスト可能性を担保する。

🟪 審査エージェントの協議ログ(採否)

エージェント / 色 主張の核心 採否
🟦 A(性能) シム単位帯[0.5,2.5] + 1フレーム相似変換 S=0.2 / SoAフラット配列 / 零アロケーション 採用(ベース) スコア最高 9点
🟩 B(体験) シームレス法則:tierIndexは見た目のみ、吸収/カメラ/フォグ/速度は半径の連続関数 移植(重要グラフト) スコア 8.5点
🟨 C(シンプル) 最小構成・コンテンツのみゲート・開発キーでテスト可能性 部分採用(テスト性キーと埋没カリングを採用) スコア 7.5点
🟩 B(体験) 3フレームの「クラスト焼き込み」パイプライン 却下 → 埋没カリング(数行)で代替

🟪 審査の結論は明快でした。🟦をベースに採用(float32精度をあらゆるスケールで保証できる唯一の案)し、🟩の「シームレス法則」を最重要グラフトとして移植🟩のクラスト焼き込みは却下して🟨の埋没カリング(数行で同じ問題が消える)を採用。そして注目すべきは——物理エンジンの不採用は、🟦🟩🟨の3案がそれぞれ独立に「ライブラリ不使用の自作アーケード物理」で一致していた点です。動体はボール1個だけという前提では、rapier(約1.7MB wasm、リスケール毎に全body teleport-scaleで破綻)も cannon-es も「無い問題を解く」だけ——3エージェントが互いを知らずに同じ結論に達したことが、その判断の確からしさを裏打ちしています。

型2: contract-freeze + 並列ストリーム + 統合者

実装フェーズの型。まず Phase 0 ですべてのモジュール間契約(イベント、型、定数、ファイルの骨格スタブ)を凍結します。各ストリームは他人のファイルを触らないことが保証されるので、5本の実装ストリームを文字通り並列で走らせられます。最後に統合者だけが main.js で配線をつなぐ。

凍結の徹底ぶりは v4 が象徴的です。Phase 0 で main.js に「形だけ一致する no-op スタブ」を各構築スロットに置き、各スタブには置き換え用の構築式と引数順をコメントで埋め込む。これにより統合は「構築式の差し替え」だけになり、呼び出し側の書き換えが一切発生しません。さらにJSバジェットは、現実的な実装を詰めた一時スパイクファイルを実際にビルドして計測(ベースライン205.42KB → スタブ込み209.52KB gz)してから 240KB を FINAL として凍結しています。

型3: adversarial review(指摘 → 独立した懐疑者が反証を試みる → 確定/棄却)

これが ultracode のキモであり、ultracode + Fable で最も見せたい部分です。レビューで上がった指摘を、そのまま信じない。指摘1件ごとに別の反証エージェント🟧(懐疑者)が立ち上がり、コードを実際に読んで「この指摘は間違っている」と反証を試みます。反証できなければ確定、できれば棄却。指摘する側(🟥)と反証する側(🟧)が、互いに独立して論理をぶつけ合うのです。

for finding in review_findings:        # 🟥 が挙げた指摘
    skeptic = spawn_independent_agent() # 🟧 独立した懐疑者
    verdict = skeptic.try_to_refute(finding)   # コードを読み、到達可能性・数値根拠・仕様適合を検査
    if verdict == REFUTED:
        reject(finding)                 # 🟧 が論破 → 偽陽性として棄却
    else:
        confirm(finding) -> fixer.queue(finding)   # 反証できず → 確定

v1 レビューでは、9件を偽陽性として棄却し、確定21件のうち16件を修正(4件は低リスクとしてスキップ)しました。棄却率は約36%です。

具体的な v1 の応酬を一つ追ってみます。

  • 🟥 指摘: 「?r= デバッグ起動でフレーム1の大量吸収が起きる(correctness)。スポーン順序を入れ替えて修正すべき」
  • 🟧 反証: 「症状は実在する。だが、スポーン内容は tierIndex_scaleExp(どちらも0)にのみ依存し、ballRadiusSim には依存しない。よって提案された順序入れ替えを適用しても同じ大量吸収が起きる——修正案は無効。むしろ ballRadiusSim=1200 で prewarm 閾値を踏み N+2 帯をロードする分、わずかに悪化する
  • 判定: 🟧 が修正案の無効性まで証明 → 棄却

🟥 の指摘を 🟧 が読み込んで、症状の実在を認めつつ提案修正の無効性まで証明してみせる。この「独立したエージェントが互いに論破を試みる」二段構えこそ、ultracode + Fable で最も伝えたい価値です。偽陽性で無駄な修正を入れて回帰を生むことを、構造的に防ぎます。

実際 v1 では、確定21件のうち16件を修正した後、最終バリデータが実プレイで回帰2件を追加発見・修正しています(ACCEL_K=22 が速度キャップを到達不能にしていた問題など)。

なお棄却率はバージョンによって大きく振れます。v2 は19件中6件を棄却、v3 は22件中16件を棄却(ただしv3は再開時の再検証でHEADとの陳腐化を多く拾った副作用が含まれます)。「36%」はあくまでv1固有の数字です。

バージョン進化 v1 → v5

各版で「オーナーが何を指示し、どこが山場だったか」を、特にバグ物語を中心に追います。

v1: 無限スケールの土台(48エージェント / 約377万トークン / 約2.5時間)

💬 実際に投げたプロンプト(/goal・追加指示)
fableの性能を見るためにブラウザ動作する3Dゲームを作りたい
塊魂のようなどんどんオブジェクトを取り込んで大きくなるとシームレスにもう一段階大きい見た目の視界とオブジェクトの大きさを変えて
取り込むスケールが大きくなってもなるべく動作が重くならないような仕組みにして欲しい
ultracodeで実行してデプロイまで完了させて欲しい
後でどんな手法で作成したか読み返せるように各タスクの思考内容、意思決定、実装方法等が分かるように全作業のログも出力して欲しい
最終的に出来たゲームはcloudflare pagesでpublic公開して欲しい
完成するまで頑張って。
トークン上限に行きそうになったら作業を止めて報告して欲しい

最初のマイルストーン。judge panel で設計を固め、5並列で実装し、4次元レビュー(正確性/パフォーマンス/ゲームプレイ/契約)+敵対的検証で締めました。

フェーズ エージェント数 サブエージェントトークン 所要時間
1. 設計(3提案+審査) 4 約18.4万 約10分
2. 実装(契約+5並列+統合) 7 約103.0万 約65分
3. レビュー(4次元→敵対的検証→修正→実機検証) 36 約255.3万 約54分
4. デプロイ+本番検証 1 - 約5分
合計 48 約377万 約2.5時間

成果物は28モジュール / バンドル585.73KB(gzip 154.31KB、Three.js込み) / 外部アセットゼロ。この時点で後述の「相似変換リスケール」と「シームレス法則」という、無限スケールを支える2本の柱が確立されました。

v2: 「銅像→月」のピボット(38エージェント / 約382万トークン / 約3時間)

💬 実際に投げたプロンプト(/goal・追加指示)
(最初の指示)かなり良い感じなのでこのまま背景と陸地や段差を追加してより塊魂に近いゲーム要素を追加して欲しい
デモなので民家の中から街の銅像を巻き込むまでをゴールにして欲しい
あとBGMとSEもつけて欲しい
ダッシュ機能とゴールまでのタイムアタック、レアアイテム巻き込み等、スコアで競える様にして欲しい
クリア時間でランクづけもして欲しい
リザルトをXでポスト出来るようにして欲しい

(プレイ後の方針転換)ごめんなさい ゴールは月になったらにしてください
ちゃんとプレイしてなかった 想像以上によく出来てました
BGMと背景や巻き込めないオブジェクトを増やしてクオリティを上げてください
Xポストも忘れずに バズって欲しい

当初のv2要件は「民家の中→街の銅像をゴール」というデモ構成でした。設計ワークフロー第1回はこのスコープで完走しましたが、批評エージェントが15件の重大問題を検出——「家屋内カメラの破綻(blocker)」「一回の吸収でドアを通れなくなるソフトロック(blocker)」「床クエリ意味論の破れ」など。

ここでオーナーが実際にプレイした上で要件を変更します。「ゴールは月に変更。家の中案は取り下げ。BGM・背景・巻き込めない大物でクオリティアップ」。

判断が見事だったのは、最もリスクが高かった地形・壁・段差システムを丸ごとキャンセルし、v1の無限平面+ティア構造を維持したまま品質向上に集中する方針に転換したこと。さらに旧設計のうちゴールに依存しない節(ダッシュ/タイマー/スコア/レア/ランク/Xポスト/BGM)をサルベージしてJSONに抽出し、新ワークフローへ入力素材として渡す——設計トークンの再利用で無駄を最小化しました。

完成した月フィナーレは、序盤から空に見える月が500m到達で降臨→接触→夜空への昇天エンディング。BGMはWebAudio完全合成のボサポップ(128BPM、成長でレイヤー解放)。

月への昇天エンディング

v3: 箱庭東京とドナック実況(40エージェント / 約607万トークン / 約6.8時間)

💬 実際に投げたプロンプト(/goal・追加指示)
スマホで実行す事が多いのでスマホ表示時の最適化をして欲しい
今はマップが無いので箱庭的に街並みが並んでて欲しい
最初は家の中の文房具や小物を拾って 大きくなったら外に出て道路にいる動物や建物を巻き込む
月ではなく最終的にスカイツリーを巻き込んだらゴール(演出は同じで良い)
東京の街並みをどんどん巻き込んで爽快感のあるゲームにしたい
最初の舞台は秋葉原の電気街にあるパーツショップから始まって 最終的に東京タワーを巻き込むまで大きくしていくイメージ
巻き込んだ時のスコアアップエフェクトと一緒に何を巻き込んだかも表示して欲しい
レアアイテムを巻き込んだ時は実際に巻き込んだアイテムをポップアップ表示してコレクション要素も出したい
プレイ中の解説者としてドナックを使って巻き込んだものの解説やゲームプレイに役立つコメントを吹き出し形式で出して欲しい

スコープが一気に広がります。スマホ最適化、箱庭東京マップ(秋葉原パーツショップ→中央通り→上野・浅草・丸の内・渋谷→スカイツリーゴール)、巻き込み名表示、レアコレクション+図鑑、そして公式キャラ「ドナック」(緑帽子のアヒル)の吹き出し実況。

設計フェーズの批評が見つけた構造的バグが技術的に面白い。「キュレートされたランドマーク/コレクティブルが、自分のティア帯ウィンドウの外では吸収/反発判定に不可視になる——ボールは東京タワーをすり抜ける」。

修正は DYNAMIC RE-BANDING。各ティア昇格時に、生きているキュレート配置すべての tierOfclamp(naturalBand, tierIndex-1, tierIndex+1) で再スタンプし、既存の64個/フレームのラウンドロビン内で償却する。ハッシュはティア変更で再構築されるので、再バンドされたスロットはタダで live ハッシュに入る。約370箇所のキュレート配置すべてに効く解決でした。

もう一つ、ゴールの曖昧性解決も記録に残っています。オーナーが「スカイツリーがゴール」と「東京タワーを巻き込むまで」の両方に言及したため、東京タワー(333m)は終盤ランドマーク、スカイツリー(634m)が最終ゴールとして両立させる解釈を採用しました。

ドナックの実況吹き出し(優先度+クールダウン制で邪魔しない)
リザルト画面とコレクション図鑑

このv3でセッショントークン上限による中断が初めて発生します。レビューのフィクサー/バリデータが上限で停止——しかしトークン回復後、Workflowの resumeFromRunId 機能でレビュー/敵対的検証フェーズのキャッシュを温存したまま再開し、フィクサーとバリデータのみ再実行。中断コストを最小化しました。

v4: リアル東京(約16エージェント / 約352万トークン / 約5時間)

💬 実際に投げたプロンプト(/goal・追加指示)
街並みデータをGoogleMapのデータを使ってゲーム画面に反映
画像テクスチャそのままだと重くなるのでGoogleマップデータから今のプリミティブモデルベースでボクセル風にマップでいい
取り込むスケールが大きくなってもなるべく動作が重くならないような仕組みにして欲しい
他のキャラクターデータもクオリティアップ出来るならして欲しい

ここが本プロジェクトの技術的ハイライトです。オーナーの指示は「Google Mapのデータで街並みをゲームに反映(テクスチャは重いのでボクセル風でOK)」+「キャラクター品質向上」。

まずライセンス判断。Google Maps Platform の ToS は地図データの派生データセット化・再配布を禁止しているため、**OpenStreetMapOverpass API, ODbL)**を採用。帰属表示のみで合法に「実在の東京の街並み」を実現できます。

設計の批評フェーズで、批評エージェントが設計の数値を信用せず自らOverpassでカウントクエリを再実行しました。その結果、設計が見積もった建物数が実測の 1.5倍ズレていることが発覚(詳細エリア建物数58,155棟など)。全予算を実測値で再導出させ、ペーシング倍率を 2.2x → 2.8x に修正しています。AIの批評が、AIの設計の数字を機械的に検算した瞬間です。

最終的に出荷されたのは:

  • OpenStreetMap由来の実在ビル14,563棟を実座標(水平1:5・高さ1:2.5)でボクセル化、すべて巻き込み可能
  • 道路12,980レコード / 河川・公園ポリゴン1,478レコード
  • 出荷バイナリ合計 約284KB gz(core 105KB + outer 181KB、ハードキャップ1,536KB)
  • ドローコール 60/72

OpenStreetMap由来の実在ビルで埋まった東京

v4実装は3回の中断を経て完走しました。①初回実行中にセッション切れでストリームが約10時間停止 → resumeFromRunId で再開、②再開後にセッション上限 → 再々開でストリーム3本完走、③統合者が上限 → 21時に再開し統合完了。各再開でPhase 0とパイプラインの結果はキャッシュから即時返却され、二重実行ゼロでした。

キャラクター品質パス

ヒーロー12種を高品質化(汎用アーキタイプの三角形上限350に対しヒーローは600へ引き上げ)、頂点AOベイク、空色リムライト。すべて独立したキルスイッチ(uRimK=0 等)付きで、オーナー承認のためA/Bスクリーンショットシートを生成しています。

v3とv4のヒーローモデル品質比較A/Bシート(AO+リム+パレット再調整)

v5: オーナーフィードバックのポリッシュ(6エージェント / 約97.7万トークン / 約100分)

💬 実際に投げたプロンプト(/goal・追加指示)
(バグ報告)なんかプレイしてみたけど 最初のステージが何にも見えない カメラ位置おかしく無い?

(追加要望)最初の店で謎の溝がある
最初はすぐ近くにパーツがある様にしてどこに行けばいいかわかる様に誘導して欲しい
レアアイテムにスタックチャンを追加して欲しい
電気街はゲーセンや家電量販店、メイドカフェ等、アキバっぽい建物も追加して欲しい
最初のお店は位置的に千石電子店を想定して欲しい
エンディングで夜景が綺麗だねと言ってるので 地球から宇宙に飛び立つ様な感じで遠くで夜景が光る地球が見える様な感じのエンディングにして欲しい

オーナーのスマホ実プレイで2件のフィードバックが来ました。一つは後述の開幕不可視バグ。もう一つが追加要望5点です。

  • 開幕の完全リニューアル: スタート店を実在の秋葉原・千石電商の位置をテーマにした「センゴク電子」に。スポーン周囲にパーツの輪+出口へ続くパーツの道+🔩誘導矢印で、転がして2秒以内に最初の吸収
  • スタックチャン: コレクション13個目としてM5Stackロボ「スタックチャン」が店の棚に(ドナックのいとこ)
  • アキバらしい建物: ゲームセンター・家電量販店・メイドカフェ・PCパーツビルが電気街に
  • 宇宙エンディング: スカイツリーを巻き込むと地球から離陸——眼下に夜の地球、日本列島に金色の街明かり

開幕。パーツの輪と🔩誘導矢印で2秒以内に最初の吸収へ
スカイツリーを巻き込んで地球から離陸する宇宙エンディング

アンカー判断が技術的に律儀でした。「実在の千石電商の位置(35.6993, 139.7713)にアンカーを動かすべきか?」を数値で検討し、現アンカーから193m(約39ゲームm)しか違わないこと、しかも動かすと実在のラジオ会館が開幕プレイレーン上に乗ってしまい、パイプライン再取得・全reconciliation値の再生成・ナビゲビリティ再ゲート・ペーシング再実行が必要になることを突き止めました。結論は「アンカーは動かさず、既存の店をセンゴク電子としてテーマだけ変える」。決定打は、現アンカー下で実在の千石電商位置がちょうど店のドアから約36ゲームmにマップされ、テーマ変更だけで地理的に正直になることでした。

技術ディープダイブ

相似変換リスケール — シームレスな無限スケールの正体

塊魂の「2cmから月まで」を、5桁のスケール差で60fpsを維持しつつシームレスに繋ぐ。これをどう実現したか。

核心: シミュレーションは常にボール半径0.5〜2.5という狭い数値帯で実行する。 ボールがこの帯の上限に達したら、ティア昇格時に全シミュレーション状態に一様な相似変換 S=0.2 を1フレームで適用します。

on TIER_UP:
    for every sim entity (ball, objects, spawner origin, particles...):
        position *= S          # S = 0.2
        radiusSim *= S
    worldScale  /= S           # 累積スケールを逆向きに更新
    # 実寸(trueRadius)は double で別管理 → この操作で不変

ここが効くのはカメラがスケール量の純関数だからです。カメラ位置・注視点・フォグ距離はすべて radiusSim(と worldScale)から計算される。だから全状態を一律に 0.2 倍したリスケールフレームは、描画結果がピクセル同一になる——遷移が完全に不可視なのです。

そして実寸は trueRadius という double で別管理radiusSim は常に [0.5, 2.5] に収まるので、Float32精度(InstancedMeshの行列は float32)が無限成長まで保たれる。月だろうが惑星だろうが、シミュレーションの数値は常に「ちょうど良いスケール」で回り続けます。

このリスケールが正しいことは v3〜v5 で繰り返し検証されています。v3 の KeyR 強制リスケール(worldScale 0.04→0.2)ではワールド描画がバイト同一、差分はHUDタイマー帯の407pxのみ。v5 では osmGround の trueRadius 再計算が 0.316406250.31640625000000017、すなわち **1 ulp(1.7e-16)**しかズレないことを r=1.5(ws 1→5)で確認しています。

シームレス法則 — ティア番号は見た目だけを駆動する

リスケールと対をなす設計原則がシームレス法則です。

吸収判定・カメラ・フォグ・速度は、すべて半径の連続関数である。ティア番号(tierIndex)は見た目(スポーン内容・パレット・演出)のみを駆動する。

つまりゲームプレイ上のいかなる量も、ティア境界で不連続にならない。これにより、閾値でのポップ/ヒッチが構造的に存在しません。v3でフォグ下限が load 半径を割る問題(FOG_FAR_MIN_M=9r<0.16m で破綻)が見つかったときも、修正は「load側に同じ下限をミラーする」(LOAD_RADIUS_MIN_M = 1.25*FOG_FAR_MIN_M)——あくまで半径について連続を保つ形でした。

v5の「謎の溝」修正も同じ思想です。OSM地表リボンを実半径 0.6〜3m で smoothstep フェードインさせ、trueRadius = radiusSim*worldScale で計算するのでリスケール不変。KeyRピクセル同一性が構造的に保証されます。

自作アーケード物理 — コア数百行、依存ゼロ

物理エンジンは使っていません。動体はボール1個だけという割り切りが効いています。コア物理(ballPhysics.js 約314行)と吸収解決(absorb.js)合わせても数百行で、依存はゼロです。前述のとおり、この「ライブラリ不使用」という結論は🟦🟩🟨の3設計エージェントが独立に一致したものでした。

  • 衝突候補の取得はティア帯別の空間ハッシュ(フラット型付き配列)でマイクロ秒オーダー
  • 取り込んだオブジェクトはボール子階層に取り付け時1回だけ行列を書き込み、埋没したら自動間引き
  • ゼロ・パー・フレーム・アロケーション(モジュールレベルのスクラッチオブジェクトで実現)

レビューではこの物理周りで実のあるパフォーマンス指摘も確定しています。🟥「InstancedPool の mesh.count が縮まず、ティアが進むごとにドローコールと退化インスタンスの頂点処理が永久に蓄積する」——🟧 がこれは反証できず確定。修正は、生きているインスタンスが0になった瞬間に mesh.countvisible を畳む、というもの。これにより当時(v1)のドローコール上限55を終盤でも守れるようになりました(その後 v2 で60、v3以降は現行の72へ引き上げ。v5フィナーレ最悪値でも70/72、街中は58/72程度に収まっています)。

一方で、前述の通り9件は棄却されています。🟥「Effects の QuadPool が毎フレーム全バッファ(約12KB)をアップロードする」という指摘は、🟧 反証エージェントが「各QuadPoolは aliveCount===0 で早期リターンし、needsUpdate はプール単位。通常のローリング時は32スロットの lines プールだけ=2KB/フレーム。12KBはティアアップ前後のサブ秒窓だけ」と数値で反証しました。

OSMパイプライン — 実データを284KBに圧縮する

v4のデータパイプラインは、再現可能な決定論的変換として scripts/osm/ に実装されています。

osm:fetch  →  再開可能なOverpass取得(UAヘッダ必須、/api/statusでスロット監視、
              429はretry-afterでリトライ、>=2s間隔、セルごとにアトミック書き込み)
osm:build  →  決定論的変換(再実行でバイト同一):
              dedupe(type,id) → マルチポリゴンの外環ステッチ → 投影 →
              カバレッジ重心クリップ → 除外ゾーン → OBB → 高さ → ボクセル量子化 →
              長屋マージ(14,546件) → クリアランスベイク(13,367 inset / 14,319 drop) →
              バンド分け → KEEP_K間引き → 16アーキタイプのコード+色 → FKT4 v1シャード
osm:verify →  予算/カウント±20%/重複ゼロ/カバレッジ/POLY u16証明/
              二回実行バイト同一/ランドマーク実距離/ナビゲビリティ → predeployゲート

なお、ポリゴンの簡略化にはラモー=ダグラス=ポイカー法(Douglas-Peucker)系の頂点削減を用い、形状を保ったまま頂点数を落としています。

注目すべきはナビゲビリティ(通行可能性)のベイク。1:5スケールでは実際の街路網がボールより狭いため、住宅道路を取り込み(出荷はしない)、道路回廊に食い込む建物OBBを最大30%まで内側に押し込むか落とす。0.5mラスタでボール半径からflood fillして、ball-radiusブラケットで95%以上の到達率をゲートにしています(r=1で100.0%、r=3で96.7%)。

ライセンス遵守もリリースゲートに組み込まれています。タイトル/リザルト画面の © OpenStreetMap contributors リンク、README/マニフェストのODbLリンクと抽出日時、そして公開リポジトリの data/osm-raw/(タグ削減済み生レスポンス7.3MB)+ scripts/osm/ が ODbL の derivative database 公開義務を構成します。osm:verify がこのフィールドの存在まで検証します。

リスケール法則は実座標タイルにもそのまま適用されます。OsmGround は親Groupの変換が scale=1/worldScale, position=-shift の純関数で、毎フレーム live state から再計算される。地表レイヤーのYオフセット(公園+0.02 / 道路+0.04 / 主要道+0.06ゲームm)も相似変換でスケールするので、リスケールのピクセル同一性が保たれます。

バグ物語 — AIが作ったものを人間が遊ぶと

v4の「開幕不可視バグ」

v5のきっかけになった、最も象徴的なバグです。オーナーがスマホで遊んで「最初のステージが何も見えない、カメラ位置おかしくない?」。カメラは正常でした。原因は3つの複合:

  1. 地表の浮かせ量 fogFar×2e-3 がフォグ9m下限により開始時0.45sim=ボール半径の90%
  2. 道路の焼き込みYオフセット 0.06ゲームm=半径の3倍 → 道路板がカメラとボールの間に
  3. JR総武線の高架リボンが実座標でスポーン直上を通過——除外ゾーンが建物のみ適用で、リボンを素通しさせていた

つまり、実在の総武線高架が2cmのボールの真上に「天井」として被さっていたわけです。OpenStreetMapの実データを使った副作用として、現実の鉄道が開幕の視界を塞ぐ——リアルデータゲームならではのバグでした。修正は、浮かせ/オフセットをボール半径10%上限でクランプ(半径連続)+パイプラインで道路/鉄道リボンを店・通り除外矩形からクリップ + osm:verify に再発ゲート追加。

批評AIが自走してOverpassを叩いた

前述のv4設計で、批評エージェントが設計の数字を信じずに自分でOverpassカウントクエリを再実行し、1.5倍の見積もり誤差を捕まえた件。これは「AIに任せて見えたこと」の核心です。指示されたわけではなく、批評の役割を与えられたエージェントが自律的に検算手段を選んだ。実装でも、ランドマークは手打ちのelement idではなく名前/タグクエリで解決し、実距離アサートで検証する方式を選んでいます(手計算は禁止)。

中断からの再開

セッショントークン上限はv3で1回、v4で3回、計4回発生しました。そのたびに resumeFromRunId でキャッシュを温存して再開し、二重実行ゼロで完走しています。長時間・大規模なエージェントワークフローが途中で死んでも回復できることの実証です。

テストと検証 — Fableの自走力の源泉

このプロジェクトで一番伝えたいのは、Fableの出力精度と自走力は「テスト能力の高さ」に支えられているという点です。作業ログを集計すると、工数の大半が「作る」ではなく「検証する」に向いていました。

  • エージェントの約71% / サブエージェントトークンの約58%が検証・レビューに投下(v1〜v3の独立レビューフェーズ集計:89/126体・約787万/約1,366万トークン。v4/v5は検証を実装ワークフローに統合)。
  • 敵対的検証で計71件の指摘を精査し、31件(44%)を偽陽性として棄却(v1 9/30・v2 6/19・v3 16/22)。「指摘→必ず別AIが反証を試みる」二段構えが、誤検出と無駄な修正による回帰の両方を防ぎます。
  • ヘッドレステスト計 約3,000アサート(最大の塊はv4のOSMデータ検証 2,376アサート)+ agent-browserによる実機検証 計25パス / スクリーンショット成果物27枚
  • KeyR強制リスケールのピクセル同一性を 1 ulp(1.7×10⁻¹⁶)まで検証osm:verify は ナビゲビリティ≥95%(実測 r=1で100% / r=3で96.7%)・二回実行バイト同一性・バンドル予算240KB を デプロイ前のALL GREENゲートとして強制します。

そして効くのが、「ビルドは通る・アサートも緑」なのに壊れている回帰を、実機テストが繰り返し自己捕捉したことです。例:

  • v1ACCEL_K=22 が終端速度をキャップ未満に固定し、巡航速度=吸収レートが暗黙に半減(ビルドもアサートも素通り)→ 最終バリデータの実プレイ計測で発覚し 45 に修正。
  • v3:成長カスケード(巨大化が速すぎる)を敵対的検証+実測で精査し、キュレート例外で抑制。デスクトップ限定のCSS重なり回帰も実機で捕捉。
  • v4→v5:開幕不可視バグ(実在の総武線高架がスポーン直上=天井)を、オーナーの実機プレイ→agent-browser再現でCONFIRMED。
  • v4設計:批評エージェントが設計の数字を信用せずOverpassを自分で叩いて再実測し、見積もりの1.5倍(建物58,155棟)を発見。

Fableは「たくさん作れる」より前に「たくさん検証して、自分の出力を疑える」。この検証駆動こそが、5バージョンを通じて精度と自走を支えた背骨でした。

AIに任せて見えたこと

5バージョン・約148エージェント・約18Mトークンを通して見えた、エンジニアリング上の発見をまとめます。

  • 敵対的検証は本当に効く: v1で指摘の36%が偽陽性として棄却されました。大規模言語モデル(LLM)レビューは「それっぽい指摘」を量産しがちですが、「🟥指摘→必ず🟧独立エージェントが反証を試みる」二段構えにすると、偽陽性の混入と、無駄な修正による回帰の両方を防げます。棄却理由が「症状は実在するが提案修正は無効」のように具体的なのがポイントです。
  • 批評エージェントは自走する: 設計の数字を信じず自分でOverpassを叩く、ランドマークをクエリで解決する——役割を与えると、検証手段を自律的に選びます。
  • 独立なのに結論が一致する: 設計の3エージェント(🟦🟩🟨)が互いを知らずに「物理ライブラリ不使用」で一致したように、独立な視点が同じ結論に収束したとき、その判断は強い。逆に割れたところ(クラスト焼き込み)こそ審査🟪が踏み込んで裁く価値がある。
  • 契約凍結が並列を可能にする: Phase 0でI/Fとスタブを凍結しておけば、5本のストリームを文字通り並列実装でき、統合は構築式の差し替えだけになる。AIの並列実行を破綻させないのは、人間の大規模開発と同じく契約でした。
  • 中断は回復可能: resumeFromRunId でセッション上限を跨いでも作業が無駄にならない。
  • そして最後は人間が遊ぶ: 総武線が天井になる開幕不可視バグは、自動検証では「コンソールエラー0件」で通過していました。オーナーがスマホで実プレイして初めて見つかった。AIの品質ゲートがどれだけ厳密でも、実機で人間が触る工程は代替できません。

まとめ

Fable Katamari は、AIにゲームを作らせる「デモ」であると同時に、マルチエージェントで実際に動くソフトウェアを作る方法論の検証でした。judge panel(🟦🟩🟨→🟪)・契約凍結並列・敵対的検証(🟥↔🟧)という3つの型、相似変換リスケールとシームレス法則という2本の技術的柱、そしてOpenStreetMapの実データを284KBに圧縮して東京を呑み込めるようにするパイプライン——どれも実在のコードと数字に裏打ちされています。

そして何より、桁違いのスケールがシームレスに繋がるあの感覚——塊魂が四半世紀前に切り拓いた発明への、技術的なオマージュです。AIが書いたものを「AIが書いた」と正直に認めた上で、その中身がこれだけ具体的に解剖できる。それ自体が、一つの結論なのだと思います。ぜひ一度、2cmのボールを転がして東京を呑み込み、地球から飛び立ってみてください。

遊ぶ: https://fable-katamari.pages.dev
ソース(MIT): https://github.com/aieo-product/fableDemoGame

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?