ChatGPT Astraで、ブラウザから遊べる3Dバトルロイヤル「LAST SIGNAL」を作ってみました。プレイヤー1人とAI23体が、島で最後のひとりを目指すゲームです。

タイトル画面から出撃、戦闘、結果表示、再戦までつながりました。ただ、作った本人が「新兵」で遊んだら、最初の試合は8秒で退場。動くゲームができたことと、遊びやすいゲームになったことは別でした。

この記事では、実際に使った実装用プロンプトとコードをもとに、どう組み立て、何を直し、何がまだ課題なのかをまとめます。制作にはAstraを使い、この記事の文章レビューはClaude Codeに依頼しています。

現在の機能・開発状況を見る / ゲームを遊ぶ

LAST SIGNALの港湾エリア。コンテナや倉庫を使って射線を切る3Dゲーム画面

最初に決めたのは「何を作らないか」

初回の範囲は、1マップのソロ対AI戦に絞りました。オンライン対戦、ログイン、課金、車両、建築クラフトは対象外です。画面上でも対戦相手をAIと明示しています。

そのうえで、「撃つ」「隠れる」「安全地帯へ先回りする」という判断がつながることを狙いました。遮蔽物、敵の音、縮小する円から次の行動を選べる構成です。

プロンプトでは、AIにゲームディレクター、3Dゲームエンジニア、テクニカルアーティスト、QAの役割を持たせ、説明だけで終わらず、実装・起動・確認・修正まで進めるように指定しました。

使用したプロンプト

実装用プロンプト全文(Markdown)

全文は長いので、ここでは設計に効いた部分を抜き出します。これは依頼時の仕様であり、そのとおりに実装した部分と、途中で変えた部分があります。変更点は次の項目にまとめました。

荒れた沿岸の研究施設島で、プレイヤー1人とAI23体が最後の1人を争う、三人称タクティカル・バトルロイヤルを作る。

外部バックエンドとAPIキーなしで動かす。将来の拡張に備え、入力、シミュレーション、描画を分けるが、未使用のネットワーク機構を先回りして構築しない。

ゲームループをReactの毎フレームのstate更新で実装しない。

開発用シミュレーションでの成功を、人間による操作確認や描画性能の検証と混同しない。

ほかにも、武器の威力・発射間隔、回復の中断条件、煙幕がAIの視線にも効くこと、再戦時に入力や音を解放することなどを具体的に書きました。「それらしい画面がある」だけで完成にしないための条件で、ここに挙げたものは現在いずれも実装済みです。

プロンプトと実装が違うところ

プロンプトは新規構成の基本をVite+TypeScript+Three.jsとしつつ、既存プロジェクトがあればそちらを優先する内容です。今回はNext.jsの既存構成にゲームを組み込みました。

物理についても、候補に挙げたRapierは採用していません。実装は地形と軸に平行な箱(AABB)の衝突判定、投擲物の反射を使う簡易方式です。汎用の剛体物理や建物の破壊までは扱っていません。

当初のプロンプトではスマホにPC向け操作の案内を出す方針でしたが、制作途中で「スマホからも操作したい」と追加依頼し、タッチ操作を実装しました。

入力・試合の状態・描画を分ける

ゲーム側の構成はNext.js 16.2.4、React 19、TypeScript、Three.js 0.186.0です。役割を次のように分けています。

担当 主なファイル 役割
入力 engine/input.ts、touch-controls.tsx、engine/touch-gestures.ts キー、マウス、指の操作を共通の入力データへ変換
試合の状態 engine/simulation.ts 移動、戦闘、AI、装備、イベント、勝敗を更新
地形と経路 engine/world.ts、engine/navigation.ts シード付き配置、歩行グリッドとA*による経路探索
描画 engine/renderer.ts Three.jsのシーン、カメラ、人物や環境の描画
音 engine/audio.ts Web Audioで効果音を合成
画面と統合 last-signal.tsx タイトル、設定、HUD、ポーズ、結果画面とゲームループを接続

キャラクターや建物は手続き的に作った形状で、人物の動きもコードによるアニメーションです。現在はローポリの形状を使った見た目になっています。

試合は30Hz、描画はブラウザのフレームに合わせる

シミュレーションは1/30秒の固定刻みで進め、描画はrequestAnimationFrameで行います。前回と今回の位置を補間して、表示をなめらかにつなぎます。

一度に追いつく更新は最大5回に制限しています。タブの切り替えなどで長く止まったあとに、過剰な更新をまとめて実行しないためです。

Reactにはタイトルや設定などの画面管理を任せ、HUDは10Hzのスナップショットで更新します。ゲームの状態すべてを毎フレームReactのstateに流す構成にはしていません。

スマホで「走ると撃てない」を直した

タッチボタンを並べた段階では、複数の操作を同時にできるとは限りませんでした。自分で触っても「走ると撃てない」「一つの動作しかできない」状態でした。

ここで確認したのは、入力とゲームルールの両方です。指の操作を受け取れていても、戦闘処理側に走行中の射撃禁止が残っていれば、弾は出ません。

修正では、走行中の射撃禁止を解除し、指ごとの担当を独立させました。移動の指、視点操作の指、射撃の指を別々に扱い、ひとつの指を離しても別の操作が止まらないようにしています。

また、取得やジャンプなどの単発操作はpointerdownで処理し、別の指で移動している最中にも反応させました。射撃ボタンは長押しとドラッグを組み合わせ、撃ちながら視点も動かせます。

テストでは入力値だけでなく、移動距離が増え、実際に発砲するところまで確認しています。ただし、確認できたのはスマホ相当の画面サイズとテスト上の同時入力までです。iPhone・Androidの実機で複数の指を同時に使う操作、発熱、長時間プレイは未検証で、引き続きの課題です。

走るとガタつく原因は、位置だけではなかった

PCでもスマホでも、走るとガビガビしてスムーズではないと感じました。

位置を補間していても、手足の動きや走行状態が急に切り替わると、見た目は滑らかになりません。そこで、手足のアニメーション位相を描画フレームごとに連続更新し、歩行・走行・停止・しゃがみを滑らかにつなぐようにしました。

スタミナが尽きる付近では、少し回復するたびに走行へ戻ると、歩行と走行が細かく入れ替わります。枯渇後は一定量まで回復してから走行を再開するよう、開始と停止のしきい値を分けました。

加えて、影の投影位置をテクセルに揃えてちらつきを抑え、カメラ判定用の箱を事前計算して、毎フレームのオブジェクト生成を減らしています。

検証で分かったことと、まだ分からないこと

同時入力と走行描画を修正した2026年9月11日時点では、型チェック、lint、ビルドに加え、27件のテストを通しました。ダメージや回復の中断、煙と遮蔽、入力の解除、スタミナ枯渇、60Hz・120Hz相当でのアニメーション位相などを確認しています。

AIだけの加速シミュレーションも使いました。修正後の記録では、シード42・103・991の試合が、それぞれ236秒・304秒・283秒で生存1人まで進みました。これら3配置では、継続的な移動指示があるのに長く停止する区間は観測されませんでした。

これは特定の配置で進行を確認した記録です。全シードでの問題不在や、人間にとって適切な難易度を保証する結果ではありません。

新兵強すぎ問題は、まだ解決していない

実プレイの録画では、最初の試合が8秒で終わりました。いちばん簡単な「新兵」設定なのに敵が強すぎる、ここで「新兵強すぎ問題」と呼んでいる状態です。新兵設定でも、装備を拾って操作に慣れる前に倒されることがあります。

この1本の動画だけで全体の難易度は断定できませんが、少なくとも「初心者が何をすればよいか分かる前に終わる」体験は改善したいところです。AIの反応時間・射撃精度に加え、開始地点の射線、初期装備、導線も見直します。

録画は約34秒・音声なし。最初の敗北から再戦までを含む実プレイです。難易度の調整が済んだ動画ではありません。

次に直したいこと

優先したいのは、いちばん簡単な新兵モードの難易度です。初見で武器を拾えるか、敵に気づけるか、逃げる余地があるかを、実プレイで確かめながら調整します。

音はWeb Audioによる簡易SEが入っています。次はBGMの導入と、射撃・被弾・リロードなどのSEの充実。外部の音源を使う場合は、利用条件を確認して記録する必要があります。

オンライン対戦も今後の候補ですが、現在は未実装です。サーバー側で試合の正しい状態を管理する仕組み、同期、遅延への対応、部屋の作成などが必要になります。入力と描画を分けたことは足場になりますが、それだけで対人戦へ切り替えられるわけではありません。

Astraで実装を進められても、どこが遊びにくいかを見つけるには実際に遊ぶ必要がありました。次は「新兵で練習できる」と言える状態を目指します。

最新の開発状況はLAST SIGNALのゲーム制作ページにまとめます。遊ぶ前に知りたい人向けの楽しみ方と、PC・スマホの操作ガイドも用意しています。