game-architecture について
Three.js または Phaser を使うブラウザーゲーム向けの設計スキルです。コアプレイ、状態とイベント管理、再起動時の後処理、公開前の検証を案内します。
主な機能
- 01コアゲームループを優先する指針
- 02EventBus によるモジュール間通信
- 03ゲーム状態と定数の一元管理
- 04オーケストレーターによる進行管理
- 05再起動に備えたクリーンアップ
- 06公開前の検証チェックリスト
活用シーン
- 01ブラウザーゲームの設計計画
- 02ゲームシステムとプレイ機能の分割
- 03再起動後のリスナーやリソース残留の防止
- 04入力、レスポンシブ対応、ビルドの確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ hungify/ playables-skills --skill game-architectureインストール後、Claude Code で/game-architectureを入力して読み込まれたか確認します。
同梱ファイルsystem-patterns.md
詳細
ゲームアーキテクチャのパターン
適切に構成されたブラウザーゲームを構築するための参考知識です。これらのパターンは、Three.js(3D)と Phaser(2D)の両方のゲームに適用できます。
参考ファイル
詳細な参考情報については、このディレクトリにある関連ファイルをご覧ください。
system-patterns.md— オブジェクトプール、デルタ時間の正規化、リソースの破棄、ウェーブ/スポーンシステム、バフ/パワーアップシステム、触覚フィードバック、アセット管理
基本原則
-
コアループを最優先する:見た目を整える前に、最小限のゲームプレイループを実装します。順序は、入力 → 移動 → 失敗条件 → スコアリング → 再スタートです。コアループが動作してから、ビジュアル、オーディオ、演出を追加します。初期スコープは小さく保ちます。シーン/レベルは1つ、メカニクスは1つ、失敗条件は1つにします。
-
イベント駆動型の通信:モジュール同士が通信のために互いをインポートすることはありません。モジュール間のメッセージはすべて、定義済みのイベント定数を使ってシングルトンの EventBus を経由します。
-
状態の一元管理:単一の GameState シングルトンに、すべてのゲーム状態を保持します。各システムは状態を直接読み取り、イベントを通じて変更します。モジュール間に状態を分散させません。
-
設定の一元管理:すべてのマジックナンバー、バランス値、アセットパス、スポーン地点、タイミング値を
Constants.jsにまとめます。ゲームロジックのファイルにハードコードされた値を含めません。 -
オーケストレーターパターン:1つの
Game.jsクラスがすべてのシステムを初期化し、ゲームの進行(起動 → ゲームプレイ → 死亡/勝利 → 再スタート)を管理して、メインループを実行します。システム自身が初期化することはありません。デフォルトではタイトル画面を設けません。ゲームプレイへ直接起動します。ユーザーから明示的に求められた場合にのみ、タイトル/メニューシーンを追加します。 -
再スタートに安全で決定論的であること:ゲームプレイは、完全な再スタートを繰り返しても問題なく動作しなければなりません。
GameState.reset()は状態を完全に初期状態へ戻します。クリーンアップ/シャットダウン時には、すべてのイベントリスナーを削除します。再スタート後に古い参照、残存タイマー、解放されていないトゥイーン、孤立した物理ボディが残らないようにします。3回連続で再スタートしてテストします。3回目の実行は1回目と同じ動作でなければなりません。 -
関心事を明確に分離する:コードを機能レイヤーごとに整理します。
core/- 基盤(Game、EventBus、GameState、Constants)systems/- エンジンレベルのシステム(入力、物理演算、オーディオ、パーティクル)gameplay/- ゲームメカニクス(プレイヤー、敵、武器、スコアリング)level/- ワールド構築(レベル構築、アセット読み込み)ui/- インターフェース(メニュー、HUD、オーバーレイ)
イベントシステムの設計
イベントの命名規則
機能領域ごとにまとめ、domain:action 形式を使用します。
export const Events = {
// Player
PLAYER_DAMAGED: 'player:damaged',
PLAYER_HEALED: 'player:healed',
PLAYER_DIED: 'player:died',
// Enemy
ENEMY_SPAWNED: 'enemy:spawned',
ENEMY_KILLED: 'enemy:killed',
// Game flow
GAME_STARTED: 'game:started',
GAME_PAUSED: 'game:paused',
GAME_OVER: 'game:over',
// System
ASSETS_LOADED: 'assets:loaded',
LOADING_PROGRESS: 'loading:progress'
};
イベントデータの契約
プリミティブ値ではなく、常に構造化データオブジェクトを渡します。
// Good
eventBus.emit(Events.PLAYER_DAMAGED, { amount: 10, source: 'enemy', damageType: 'melee' });
// Bad
eventBus.emit(Events.PLAYER_DAMAGED, 10);
状態管理
GameState の構造
状態を明確なドメインごとに整理します。
class GameState {
constructor() {
this.player = { health, maxHealth, speed, inventory, buffs };
this.combat = { killCount, waveNumber, score };
this.game = { started, paused, isPlaying };
}
}
ゲームの進行
2D と 3D の両方のゲームに共通する標準的な進行です。
Boot/Load -> Gameplay <-> Pause Menu (if requested)
-> Game Over -> Gameplay (restart)
デフォルトではタイトル画面を設けません。 ゲームはゲームプレイへ直接起動します。Play.fun ウィジェットがゲーム画面上部のデッドゾーンでスコア表示、リーダーボード、ウォレット接続を処理するため、ゲーム内のスコア HUD は不要です。ユーザーから明示的に求められた場合にのみ、タイトル/メニューシーンを追加します。
よくあるアーキテクチャ上の落とし穴
- 物理ボディの接続漏れ — 静的な物理ボディ(地面や壁など)を作成しても、
physics.add.collider()またはphysics.add.overlap()を使ってほかのボディに接続しなければ、ゲームプレイに影響しません。境界や障害物には、相互作用するエンティティとの衝突設定を明示的に行う必要があります。静的なボディを作成したら、すぐにコライダーの呼び出しを追加してください。 - 重なった表示オブジェクトに操作要素が遮られる — UI(ボタンやメニューなど)を作成する場合、シーンリストで最前面にある表示オブジェクトがポインターイベントを受け取ります。操作要素を装飾レイヤーの背後に隠さないでください。ビジュアル要素自体を操作可能にするか、ヒット領域の上に何も描画されないようにしてください。
- ゲームプレイより先に演出を追加する — コアとなるゲームループが動作する前に、パーティクルや画面の揺れ、トランジションを追加するのは、よくある時間の浪費です。まずは入力 → アクション → 失敗条件 → スコア計算 → 再スタートが機能するようにしてください。それ以外はすべて仕上げの演出です。
- 再スタート時のクリーンアップ漏れ —
shutdown()でイベントリスナーの削除、タイマーの破棄、リソースの解放を忘れると、再スタート後にゴースト動作やイベントの二重発火、メモリリークが発生します。
出荷前の検証チェックリスト
ゲームを完成とみなす前に、以下のすべてを確認してください。
- コアとなるゲームループ — プレイヤーがゲームを開始してプレイし、勝敗が決まり、結果を確認できる
- 再スタート — 3回連続で、同じ動作を保ったまま問題なく動作する
- モバイル入力 — タッチ/タップ/スワイプ/ジャイロが動作する。タップ領域は最低44px
- デスクトップ入力 — キーボードとマウスが動作する
- レスポンシブ対応 — ウィンドウのサイズ変更時にキャンバスが正しくリサイズされる
- 定数 — ゲームロジックにハードコードされたマジックナンバーがない
- EventBus — 通信にモジュール間の直接インポートを使用していない
- クリーンアップ — シャットダウン時にすべてのリスナーが削除され、リソースが解放される
- ミュート切り替え —
mute-buttonルールを参照 - デルタベース — すべての移動でフレーム数ではなくデルタタイムを使用する
- ビルド —
npm run buildがエラーなしで成功する - エラーなし — 実行時に捕捉されない例外やコンソールエラーがない