1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ブラウザで動くFPSゲーム「BANG BANG」をClaude Codeで開発した

はじめに

Claude Codeを活用して、ブラウザで動くサイバーパンクFPSゲーム「BANG BANG」を個人開発しました。

インストール不要で、URLを開くだけでPC・スマホどちらでもプレイできます。

🎮 プレイはこちらhttps://code-worldweb-production.up.railway.app/world
🛠️ リポジトリhttps://github.com/EGAMIJUN/code-world

私は.NETでの業務システム開発経験はあるものの、React・Three.js・ゲーム開発はほぼ未経験です。実装の大部分はClaude Codeに任せ、自分は要件定義・レビュー・プレイテストに集中するスタイルで開発しました。

この記事では、ゲームの内容、技術スタック、実装した機能、開発中にハマったポイントを紹介します。

本記事は実体験をもとに執筆しています。コードの大部分はClaude Codeによる生成ですが、動作検証・プレイテストは人間が行っています。


どんなゲームか

「BANG BANG」は、サイバーパンク風の都市を舞台にしたウェーブ制FPSです。

主な機能

機能 内容
FPS基本操作 マウス視点操作(Pointer Lock)、WASD移動、射撃、リロード、ADS
敵AI 挟み撃ち・グレネード投擲・グループ連携などの戦術行動
難易度ティア grunt / sniper / heavy の3段階(HP 60/90/320)
縦方向マップ 侵入可能な建物20棟以上、ハシゴで屋上に登れる
物理 重力、落下ダメージ
ウェーブシステム Wave進行で敵が増加、全Wave制圧でミッションクリア
マルチプレイヤー ネイティブWebSocket(Bun/Hono)によるリアルタイム位置同期
モバイル対応 画面ドラッグ視点・バーチャルスティック・専用ボタン
設定 マウス感度スライダー、設定モーダル
HUD ミニマップ、HP・弾数・スコア表示

操作方法

PC

  • WASD / 矢印キー:移動
  • マウス:視点
  • クリック:射撃
  • E:ハシゴ / エレベーターで昇降

スマホ

  • 左下スティック:移動
  • 画面右側をドラッグ:視点
  • ハシゴに近づくと「↑ 登る」ボタンが表示

技術スタック

すべてTypeScriptで統一したモノレポ構成です。

全体

レイヤー 技術
フロントエンド Next.js 15 / React / Three.js
バックエンド Hono(Bunネイティブ WebSocket使用) / 自前セッション認証
ジョブキュー BullMQ
コード実行 Judge0 + Docker(構成定義済み・現状未稼働)
DB PostgreSQL / Drizzle ORM(本番ホスト:Neon推奨)
キャッシュ Redis(本番ホスト:Upstash推奨)
モノレポ Turborepo + Bun
Lint / Format Biome
テスト Vitest
CI/CD GitHub Actions + CodeQL
デプロイ Railway(mainマージで自動デプロイ)

モノレポ構成

code-world/
├── apps/
│   ├── web/       # Next.js フロントエンド(ゲーム本体)
│   ├── api/       # Hono APIサーバー
│   └── executor/  # ジョブ実行ワーカー
└── packages/
    ├── db/        # DBスキーマ・マイグレーション
    ├── types/     # 型定義の共有
    ├── ui/        # UIコンポーネント
    └── config/    # 共通設定

packages/types で型をフロントエンドとバックエンドで共有しているため、API変更時はフロント側がコンパイルエラーで検知できます。AIにコードを書かせる開発スタイルでは、この型のガードレールが特に有効でした。

データフロー

ブラウザ → Hono API → PostgreSQL (Neon)
              ↓
           BullMQ → Executor → Judge0 (Docker)
              ↓
           WebSocket(Bun/Hono) → ブラウザへリアルタイム通知

重い処理はBullMQ経由でワーカーに逃がし、APIサーバーをブロックしない構成にしています。


ゲーム部分の実装(Three.js)

ゲーム本体は ThreeWorld.tsx という約12,000行のコンポーネントに実装されています。

基本システム

  • 視点操作:Pointer Lock APIでマウスをキャプチャし、感度設定・スムージング付きでカメラを回転
  • 移動:デルタタイムベースのWASD移動(フレームレートに依存しない)
  • 射撃判定:レイキャストで弾道と命中を判定
  • 建物との衝突:AABB(軸並行境界ボックス)方式
  • 描画最適化:遠景スカイライン(背景ビル群のシルエット)はInstancedMeshで一括描画

敵AIの戦術行動

敵は単純に突っ込んでくるのではなく、以下の戦術を取ります。

  • 複数の敵が左右に分かれて回り込む(挟み撃ち)
  • 遮蔽物に隠れたプレイヤーへのグレネード投擲
  • グループ連携(プレイヤーの最終確認位置を味方と共有して協調追跡)

縦方向マップ

  • 20棟以上の建物に侵入可能
  • ハシゴで屋上にアクセス(PC:Eキー / スマホ:専用ボタン)
  • 建物入口には発光デカール、ハシゴ前には「[E] CLIMB」のヒント表示
  • 重力と落下ダメージを実装

レンダリング品質

  • ACESFilmicトーンマッピング + SRGB色空間で映画的な色合いに
  • PBRマテリアル + プロシージャルテクスチャ(CanvasTexture)で遠景のモアレを除去
  • DirectionalLight + シャドウマップ、HemisphereLightで陰影を表現

パフォーマンス最適化

スマホでFPSが20を下回ったため、以下を実施しました。

  • 照準UI用レイキャストを4フレームに1回へ削減(射撃判定は毎フレーム維持)
  • 街灯ヘッドをemissive化しPointLightを削減
  • 毎フレームの不要なsetState呼び出しを排除
  • pixelRatioの上限をモバイル1.25 / デスクトップ1.75に調整

結果、モバイルでも60FPS近くまで回復しました。


モバイル対応

モバイルFPSの定番方式に倣い、以下のように実装しています。

  • 視点操作:移動スティックとボタン以外の画面領域をドラッグすると視点が回る
  • 実装方式:タッチリスナーをキャンバス側に張り、操作UIは自前でタッチを捕捉。pointer-events: none のHUDはタッチを透過させる
  • マルチタッチ:左指で移動・右指で視点の同時操作に対応
  • コンテキストボタン:ハシゴに近づいた時だけ「登る」ボタンを表示

開発スタイル:Claude Codeとの分業

開発は以下の分業で進めました。

人間:要件をテキストで整理 → プロンプトとして渡す
Claude Code:実装、ビルド・lint確認、ブランチ作成、PR作成
人間:PRレビュー、マージ、本番でプレイテスト、フィードバック

PR数は35を超え、すべてClaude Codeが作成したものです。GitHub ActionsのCIを通し、mainへのマージでRailwayに自動デプロイされる構成にしています。

要望は「敵の動きをもっと戦術的に」「屋上に登れるようにしたい」のような自然言語レベルで十分機能しました。一方で、後述の通りAI任せ特有のハマりどころもありました。


ハマったポイントと対策

1. スタックPR問題(最重要)

症状:PRをマージしても本番に反映されない。

原因:Claude Codeが新しいブランチを「前のfeatureブランチ」から切っていたため、PRが縦に積み重なり、mainには古い差分しか入っていなかった。

main(古いPRで停止)
 └─ feat/A
     └─ feat/B
         └─ feat/C  ← これをマージしてもmainは更新されない

対策:ブランチ作成前に必ず以下を実行させる。

git checkout main && git pull

プロンプトのテンプレートにこの2行を含めることで再発しなくなりました。Claude Codeを使った開発では特に注意が必要なポイントです。

2. スポーン位置が建物内部にあり操作不能になる

症状:全プラットフォームで移動入力が効かなくなった(視点操作だけは可能)。

原因:プレイヤーのスポーン座標が建物のAABB内部にあり、衝突判定がすべての移動を弾いていた。

対策:スポーン位置を建物外に変更し、「現在壁の中にいる場合は移動を強制許可する」フェイルセーフを追加。

3. 衝突判定が2Dで屋上を歩けない

症状:ハシゴで屋上に登った直後に落下扱いになりダメージを受ける。

原因:壁の衝突判定が高さを無視した2D実装で、建物のフットプリント全体が全高でブロック扱いになっていた。

対策:衝突判定を高さ対応に変更(壁の上端が「足元 + ステップ高」を超える場合のみブロック)。ハシゴの着地点も床面にスナップするよう修正。


AIに任せた開発で感じたこと

型システムが品質を担保する

TypeScriptですべてのレイヤーを統一したことで、AIが生成したコードの不整合はコンパイル時に検出できました。型が通っていればおおむね動作する、という安心感は大きかったです。

アーキテクチャの理解は人間側に必要

実装の詳細は把握していなくても開発は進みますが、「なぜモノレポか」「なぜ型を共有するか」「なぜワーカーを分離するか」といった構成判断は人間が理解しておく必要があります。問題が起きた際の切り分けと、AIへの指示の精度に直結します。

修正依頼は1テーマに絞る

複数のバグ修正と新機能追加を1つのプロンプトに詰めると、問題の切り分けが困難になります。「バグ修正のPR」と「新機能のPR」を分けることで、安定して開発が回るようになりました。


まとめ

  • Three.js + Next.jsで、ブラウザだけで動く本格FPSは個人開発でも実現可能
  • TypeScriptモノレポ+型共有はAI駆動開発と相性が良い
  • Claude Codeにブランチを切らせる際は git checkout main && git pull を徹底する
  • 衝突判定・スポーン位置など、3D空間のバグはAIへの報告(再現状況の言語化)が重要
  • 実装はAIに任せられるが、アーキテクチャの判断と検証は人間の仕事

興味があればぜひ遊んでみてください。スマホでもプレイできます。

🎮 https://code-worldweb-production.up.railway.app/world
🛠️ https://github.com/EGAMIJUN/code-world


Qiita Tech Festa 2026 参加記事:「【2026年上半期】AI活用を振り返ろう!」「この記事誰得? 私しか得しないニッチな技術で記事投稿!」「お題は不問!Qiita Tech Festa 2026で記事投稿!」

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?