プロジェクト概要
FIREWALL DRAW はマウスで壁(ファイアウォール)を描いてCPUを守るタワーディフェンスゲーム。Phaser 3 で構築され、ブラウザ上で動作する。
ゲーム内容
マウスドラッグで壁を描き、四方から侵入するウイルス(敵)からCPUを防衛。10ステージ構成、各ステージに複数ウェーブ。
難易度
ノーマル / ハードの2難易度。ハードは壁の長さ制限・敵数増加・CPU HP減少。
永続化
localStorageにセーブ。復活の呪文(Base64)でデータ共有可能。オンラインランキング対応。
技術スタック
| 技術 | バージョン | 用途 |
|---|---|---|
| Phaser | 3.80.0 | ゲームフレームワーク(CDN読み込み) |
| JavaScript | ES6+ | 実装言語 |
| localStorage | Web API | セーブデータ永続化 |
| Fetch API | Web API | ランキングAPI通信 |
| Cloudflare Workers | - | ホスティング + ランキングAPI |
設計上の特徴
file:// プロトコルでも動作するよう、3段階のフォールバック戦略を採用:1. 画像 → AssetManager がプレースホルダーを動的生成
2. JSON → DefaultData.js のハードコードデータを使用
3. SE → 静かにスキップ(エラーなし)
GameScene と UIScene は並列実行され、Phaserのイベントシステムで疎結合に通信。
フォルダ構造
ファイル役割一覧
マネージャークラス
| ファイル | 役割 |
|---|---|
AssetManager.js | 画像存在チェック → プレースホルダー自動生成。ボタン・HPバーの統一生成 |
PlaceholderConfig.js | 各敵・UI・壁のプレースホルダーパラメータ(色・形・サイズ・ラベル) |
SaveManager.js | localStorage保存/読み込み、マイグレーション、復活の呪文(Base64) |
SoundManager.js | SE再生(存在チェック・連打制限50ms)、音量・ミュート管理 |
RankingManager.js | Cloudflare Workers APIへのスコア送信・ランキング取得(タイムアウト8秒) |
データ
| ファイル | 役割 |
|---|---|
DefaultData.js | 難易度設定・キャラクター5種・敵10種・壁5種・ステージ10個・アップグレード4種のフォールバック |
enemies.json | 敵パラメータ定義(HP、速度、報酬、特殊能力) |
walls.json | 壁パラメータ定義(ダメージ、色、解放条件) |
stages.json | ステージ定義(ウェーブ構成、敵出現パターン) |
スクリプト読み込み順序
index.html での読み込み順(依存関係に基づく):
<!-- 1. Phaser本体(CDN) -->
<script src="https://cdn.jsdelivr.net/npm/phaser@3.80.0/dist/phaser.min.js"></script>
<!-- 2. グローバル定数・デフォルトデータ -->
<script src="js/data/DefaultData.js"></script>
<!-- 3. ユーティリティ(依存なし) -->
<script src="js/utils/SaveManager.js"></script>
<script src="js/utils/RankingManager.js"></script>
<script src="js/utils/SoundManager.js"></script>
<!-- 4. アセット管理 -->
<script src="js/PlaceholderConfig.js"></script>
<script src="js/AssetManager.js"></script>
<!-- 5. シーン(11個) -->
<script src="js/scenes/BootScene.js"></script>
... (計11ファイル)
<!-- 6. エントリーポイント(最後) -->
<script src="js/main.js"></script>
アーキテクチャ
全体構成
┌─────────────────────────────────────────────────────┐
│ index.html │
│ └── main.js (GAME_CONFIG / GameData / Phaser起動) │
└─────────┬───────────────────────────────────────────┘
│
┌─────┴─────────────────────────────┐
│ マネージャー層 │
│ ┌──────────┐ ┌──────────────┐ │
│ │AssetMgr │ │ SoundMgr │ │
│ │画像+PH │ │ SE再生 │ │
│ └──────────┘ └──────────────┘ │
│ ┌──────────┐ ┌──────────────┐ │
│ │SaveMgr │ │ RankingMgr │ │
│ │localStorage│ │ API通信 │ │
│ └──────────┘ └──────────────┘ │
└─────┬─────────────────────────────┘
│
┌─────┴─────────────────────────────┐
│ シーン層 │
│ │
│ Boot → Preload → Title │
│ ├→ Help │
│ ├→ Data │
│ └→ StageSelect │
│ └→ Briefing │
│ └→ Game │
│ ┌─────┤ ┌────┐ │
│ │UI(並列)│ │ │
│ └─────┘ └────┘ │
│ └→Result │
│ └→Rank │
└─────┬─────────────────────────────┘
│
┌─────┴─────────────────────────────┐
│ データ層 │
│ DefaultData.js ←→ *.json │
│ PlaceholderConfig │
└───────────────┬───────────────────┘
│
┌───────────────┴───────────────────┐
│ 外部サービス │
│ localStorage Cloudflare Workers │
│ jsDelivr CDN (Phaser 3.80.0) │
└───────────────────────────────────┘
シーン遷移
scene.launch('UIScene') で UIScene を並列起動。イベント駆動で通信。
Title からの分岐:
マネージャーパターン
AssetManager(インスタンス型)
各シーンで new AssetManager(scene) として生成。画像の存在チェックとプレースホルダー自動生成を一元管理。
const assetManager = new AssetManager(this);
// 画像があればSprite、なければGraphicsでプレースホルダー
const sprite = assetManager.getSprite(x, y, 'enemy_bug', config);
// UIボタン生成・HPバー生成
const button = assetManager.createButton(x, y, 'btn_start', config);
const hpBar = assetManager.createHPBar(x, y, width, height);
SaveManager(静的クラス)
全シーンから SaveManager.method() でアクセス。localStorageを使ったセーブデータ管理。
SoundManager(インスタンス型)
SE再生前に3段階チェック:
play(key) 呼び出し
├─ ミュート状態? → return null
├─ 連打制限(50ms以内)? → return null
├─ cache.audio.exists(key)? → return null
└─ 再生成功 → Sound オブジェクト返却
RankingManager(静的クラス)
Cloudflare Workers APIとの通信。AbortControllerで8秒タイムアウト。
POST /submit → スコア送信
GET /rankings → トップ20取得
グローバル状態
GAME_CONFIG(定数)
GAME_CONFIG = {
WIDTH: 800, HEIGHT: 600,
UI_TOP_HEIGHT: 50, UI_BOTTOM_HEIGHT: 50,
GAME_AREA_TOP: 50, GAME_AREA_BOTTOM: 550,
CPU_X: 400, CPU_Y: 300
}
DIFFICULTY_SETTINGS(定数)
| パラメータ | ノーマル | ハード |
|---|---|---|
| 壁の最大長さ | 300px | 200px |
| 敵数倍率 | x1.0 | x1.5 |
| CPU HP | ステージ定義値 | 定義値 - 2(最低1) |
フォールバック戦略
assets/data/*.json と DefaultData.js は常に同じ内容を維持すること。
| 対象 | 正常時 | 失敗時 |
|---|---|---|
| 画像 | textures.exists(key) → Sprite生成 | AssetManager が Graphics API でプレースホルダー描画 |
| JSON | cache.json.get(key) → データ使用 | DEFAULT_DATA のハードコードデータ使用 |
| SE | cache.audio.exists(key) → 再生 | 静かにスキップ(エラーなし) |
シーン詳細
| # | シーン | 主な役割 | 遷移先 |
|---|---|---|---|
| 1 | BootScene | 最小ローディング表示 | → Preload |
| 2 | PreloadScene | 全アセット読み込み + エラー記録 | → Title |
| 3 | TitleScene | タイトル画面・難易度選択 | → StageSelect / Help / Data |
| 4 | StageSelectScene | ステージ選択(2x5グリッド) | → Briefing |
| 5 | BriefingScene | 敵情報表示 + キャラ選択 + コインアンロック | → GameScene |
| 6 | GameScene | メインゲームロジック | → Result |
| 7 | UIScene | HUD層(GameSceneと並列) | - |
| 8 | ResultScene | クリア/ゲームオーバー結果 | → Ranking / Title |
| 9 | RankingScene | オンラインランキング | → Title |
| 10 | HelpScene | ヘルプ(3ページ) | → Title |
| 11 | DataScene | 復活の呪文・データ管理 | → Title |
GameScene(メインゲームロジック)
初期化
// シーン間データ受け取り + アップグレード + キャラクター補正反映
const charMods = CHARACTER_DATA[charId].modifiers;
this.maxWalls = Math.max(1, 3 + upgrades.wall_count + charMods.maxWalls);
this.wallDuration = Math.max(2000, 5000 + (upgrades.wall_duration * 1000) + charMods.wallDuration);
this.wallDamageMultiplier = 1 + (upgrades.wall_damage * 0.2) + charMods.wallDamageMultiplier;
this.cpuMaxHp = stageData.cpuHp + (upgrades.cpu_hp * 2) + charMods.cpuHp;
壁描画システム
マウスダウン → ドラッグ開始地点記録
マウスムーブ → ライン描画(最大長制限あり)
マウスアップ → 壁オブジェクト生成
├─ 物理ボディ設定(静的)
├─ タイマー(wallDuration後に消滅)
├─ 壁タイプ別の色・エフェクト
└─ SE再生(draw_wall)
イベント発火
| イベント | タイミング | データ |
|---|---|---|
cpuDamaged | CPU被ダメージ時 | { hp, maxHp } |
scoreChanged | スコア変動時 | { score } |
waveCleared | ウェーブクリア時 | { wave, totalWaves } |
UIScene(HUD並列実行)
GameSceneと並列実行。イベント購読でUI更新:
gameScene.events.on('cpuDamaged', (d) => this.updateHP(d.hp, d.maxHp));
gameScene.events.on('scoreChanged', (d) => this.updateScore(d.score));
gameScene.events.on('waveCleared', (d) => this.updateWave(d.wave, d.totalWaves));
| 要素 | 位置 | 内容 |
|---|---|---|
| HPバー | 上部左 | CPU残りHP(色変化あり) |
| スコア | 上部中央 | 現在スコア |
| ウェーブ | 上部右寄り | Wave 2/5 形式 |
| ミュート | 右上 | S/M 切り替えボタン |
| ポーズ | 右上 | ポーズボタン → 音量調整(5段階)+ 再開/リトライ/タイトルへ |
| 壁選択 | 下部 | 解放済み壁タイプ切り替え |
ゲームデータ
キャラクター(5種類)
ステージ開始前のブリーフィング画面で選択可能。スタンダード以外はコインで解放。
| ID | 名前 | タイプ | 壁数 | 壁持続 | CPU HP | 壁ダメ | コスト |
|---|---|---|---|---|---|---|---|
standard | スタンダード | バランス型 | ±0 | ±0 | ±0 | ±0 | 無料 |
fortress | フォートレス | 耐久型 | ±0 | -1秒 | +3 | ±0 | 200 |
multicore | マルチコア | 壁攻勢型 | +1 | ±0 | -2 | ±0 | 300 |
chrono | クロノ | 持久型 | -1 | +2.5秒 | ±0 | ±0 | 400 |
striker | ストライカー | 攻撃型 | ±0 | ±0 | -2 | x1.4 | 500 |
キャラクター画像
各キャラクターは512×512pxの2×2グリッドスプライトシートで管理。各セル256×256pxのうち中央250×250pxを表示領域として使用。
| フレーム | 位置 | 表情 | HP条件 |
|---|---|---|---|
| 0 | 左上 | happy | 70%以上 |
| 1 | 右上 | worried | 50〜70% |
| 2 | 左下 | scared | 25〜50% |
| 3 | 右下 | critical | 0〜25% |
敵データ(10種類)
基本敵(Phase 1)
| ID | 名前 | HP | 速度 | 報酬 | 特殊能力 |
|---|---|---|---|---|---|
bug_small | 小型バグ | 10 | 80 | 5 | なし |
bug_medium | 中型バグ | 25 | 60 | 15 | なし |
worm | ワーム | 15 | 120 | 10 | なし(高速) |
trojan | トロイの木馬 | 50 | 40 | 30 | なし(高HP) |
ransom | ランサムウェア | 80 | 50 | 50 | なし(最高HP) |
特殊敵(Phase 2)
| ID | 名前 | HP | 速度 | 報酬 | 特殊能力 |
|---|---|---|---|---|---|
bomber | ボマー | 20 | 80 | 25 | 壁に触れると爆発して壁を破壊 |
shield | シールド | 15 | 100 | 35 | 最初の壁ダメージを無効化 |
spawner | スポナー | 40 | 50 | 40 | 撃破時に小型バグを複数生成 |
stealth | ステルス | 12 | 120 | 30 | 一定間隔で透明化 |
dasher | ダッシャー | 25 | 80 | 30 | 突然加速して壁をすり抜ける |
壁データ(5種類)
| ID | 名前 | 色 | ダメージ | 特殊効果 | 解放 | コスト |
|---|---|---|---|---|---|---|
basic | 基本壁 | #00aaff | 10 | - | 最初から | 無料 |
fire | 炎の壁 | #ff6600 | 15 | DoT 3/秒 | ステージ3 | 300 |
ice | 氷の壁 | #00ffff | 5 | 減速 80% | ステージ5 | 500 |
thunder | 雷の壁 | #ffff00 | 20 | - | ステージ7 | 700 |
poison | 毒の壁 | #9900ff | 8 | DoT 5/秒 | ステージ10 | 1000 |
ステージデータ(10ステージ)
| ST | 名前 | CPU HP | 報酬 | Wave数 |
|---|---|---|---|---|
| 1 | はじまりの防衛 | 10 | 100 | 4 |
| 2 | 二方向からの脅威 | 10 | 150 | 4 |
| 3 | 炎の壁を手に入れろ | 12 | 200 | 5 |
| 4 | 高速侵入者 | 10 | 250 | 5 |
| 5 | 氷結防衛線 | 15 | 300 | 5 |
| 6 | 爆破工作員 | 12 | 350 | 6 |
| 7 | 雷撃防壁 | 15 | 400 | 6 |
| 8 | ステルスの脅威 | 12 | 450 | 7 |
| 9 | 総力戦 | 18 | 500 | 7 |
| 10 | 最終防衛 | 20 | 1000 | 8 |
ウェーブ構造の例(ステージ1)
{
id: 1, name: "はじまりの防衛",
cpuHp: 10, reward: 100, targetWalls: 15,
waves: [
{ enemies: "bug_small:5", spawnInterval: 1500, directions: ["right","top"] },
{ enemies: "bug_small:8", spawnInterval: 1200, directions: ["right","top","left"] },
{ enemies: "bug_small:5,bug_medium:2", spawnInterval: 1000, directions: ["right","top","left"] },
{ enemies: "bug_small:8,bug_medium:3", spawnInterval: 800, directions: ["right","top","left","bottom"] }
]
}
アップグレード(4種類)
| ID | 名前 | 最大Lv | コスト推移 | 効果 |
|---|---|---|---|---|
wall_duration | 壁持続時間 | 5 | 100→200→400→800→1600 | 壁の消滅時間を延長 |
wall_damage | 壁ダメージ | 5 | 150→300→600→1200→2400 | ダメージ+10%/Lv |
wall_count | 同時壁数 | 2 | 500→1500 | 同時設置数+1 |
cpu_hp | CPU HP | 5 | 100→200→400→800→1600 | 最大HP+2/Lv |
セーブデータ構造
{
normal: {
clearedStages: [1, 2, 3],
highScores: { 1: 1500, 2: 2000, 3: 800 }
},
hard: {
clearedStages: [],
highScores: {}
},
coins: 2500,
unlockedWalls: ["basic", "fire"],
unlockedCharacters: ["standard", "fortress"],
upgrades: {
wall_duration: 2, wall_damage: 1,
wall_count: 1, cpu_hp: 2
},
selectedCharacter: "fortress"
}
アセット一覧
背景画像


敵スプライト(10種類)










CPUキャラクター スプライトシート(5種)
各キャラクター別の2×2グリッドスプライトシート(512×512, 表示領域250×250/セル):





CPU表情 汎用フォールバック(4段階)
キャラスプライトシート未配置時に使用される汎用画像:




UIパーツ








効果音(17個)
| カテゴリ | キー | タイミング |
|---|---|---|
| 壁 | draw_wall | 壁描画完了 |
draw_cancel | 壁キャンセル | |
| 敵 | hit | 壁にヒット |
kill | 撃破 | |
bomber_explode | ボマー爆発 | |
shield_break | シールド破壊 | |
spawner_spawn / stealth_toggle / dasher_dash | 特殊能力発動 | |
| 進行 | cpu_damage | CPU被ダメージ |
wave_start / wave_clear | ウェーブ開始・クリア | |
stage_clear | ステージクリア | |
game_over | ゲームオーバー | |
| UI | button_click / button_hover | ボタン操作 |
coin_reward / new_record | コイン獲得・新記録 |
JSONデータファイル
| ファイル | 内容 | フォールバック |
|---|---|---|
assets/data/enemies.json | 敵10種の定義 | DEFAULT_DATA.enemies |
assets/data/walls.json | 壁5種の定義 | DEFAULT_DATA.walls |
assets/data/stages.json | 10ステージの定義 | DEFAULT_DATA.stages |
プレースホルダーシステム
画像ファイルが存在しない場合、AssetManager が Phaser の Graphics API でプレースホルダーを動的生成。パラメータは PlaceholderConfig.js で定義。
| 形状 | 用途 | 例 |
|---|---|---|
circle | 敵キャラクター | 色付き円 + ラベル |
rect | UI要素・ボタン | 色付き矩形 + ラベル |
triangle | 警告マーク | 色付き三角形 |
敵プレースホルダー色
| 敵ID | 色 | サイズ |
|---|---|---|
| bug_small | 緑 | 24px |
| bug_medium | 緑 | 32px |
| worm | 黄 | 28px |
| trojan | 赤 | 36px |
| ransom | 紫 | 40px |
| bomber | 橙 | 28px |
| shield | 青 | 30px |
| spawner | 桃 | 34px |
| stealth | 灰 | 26px |
| dasher | 水 | 28px |
依存関係マトリックス
| ファイル | 依存先 |
|---|---|
main.js | DefaultData, SaveManager |
GameScene.js | AssetManager, SoundManager, SaveManager, DefaultData, GameData, DIFFICULTY_SETTINGS, CHARACTER_DATA |
UIScene.js | AssetManager, SoundManager, GameScene.events, GAME_CONFIG |
BriefingScene.js | SoundManager, SaveManager, CHARACTER_DATA, DIFFICULTY_SETTINGS |
ResultScene.js | AssetManager, SoundManager, SaveManager, RankingManager |
RankingScene.js | RankingManager, DefaultData |
PreloadScene.js | DefaultData(フォールバック時) |
TitleScene.js | SaveManager, AssetManager, SoundManager |
StageSelectScene.js | SaveManager, AssetManager, SoundManager |
AssetManager.js | PlaceholderConfig |
SaveManager.js | localStorage API |
RankingManager.js | Fetch API |
SoundManager.js | Phaser Sound Manager |