Open1
陣地広げていく系のゲーム作る

ゲーム概要
まずは小規模で動かしたい
ジャンル
- 3D陣取りアクションゲーム
目的
- プレイヤーはマップ上のタイルを爆弾で自陣の色に染め、敵よりも多くのタイルを占領することを目指す。
- 制限時間内に、より多くのタイルを占領した陣営が勝利となる。
- 敵キャラクターはプレイヤーのタイル占領を妨害し、プレイヤーや自陣営のタイルを攻撃してくる。キャラクターが倒されても一定時間後に復活する(プレイヤーも同様)。
2. ゲームの構成要素と仕様
2.1. プレイヤーキャラクター
- 操作
- ユーザーがキーボードまたはゲームパッドで操作
- 移動
- 上下左右に移動
- アクション
- 爆弾を設置
- 能力
- HP
-
CharacterStatsSO
で定義された最大HPを持つ。 - HPが0になっても、
LevelConfigSO
で定義されたリスポーン時間後に初期位置付近で復活。
-
- HPの変動
-
ReactiveProperty<int>
で管理・通知。
-
- 移動速度
-
CharacterStatsSO
で定義された速度で移動。
-
- 爆弾設置
-
BombStatsSO
で定義されたクールダウン時間 (playerCooldown
) を経て、自身の足元に爆弾を設置。
-
- HP
- ビュー
-
PlayerPresenter
がアタッチされたGameObjectで表現。 -
PlayerInputView
を通じて入力受付。
-
2.2. 敵キャラクター
- AI制御
-
EnemyPresenter
によって制御。 - 以下のAIステートに基づき行動:
-
Idle
(待機): 周囲に優先ターゲット(プレイヤーや占領すべきタイル)がいない場合のフォールバック状態。短時間で他のステートへ再評価。 -
CapturingTile
(タイル占領優先): プレイヤーが安全な距離にいる、または視界にいない場合、最も近い(または戦略的に価値のある)ニュートラルタイルや敵(プレイヤー)タイルに向かい、爆弾を設置して自陣を広げようとする。 -
ApproachingPlayer
(プレイヤーへ接近): プレイヤーが中距離にいて、かつタイル占領よりもプレイヤーへの攻撃を優先すべき状況(例:プレイヤーが自陣深くに侵入、敵のHPが十分にある)の場合、プレイヤーに向かって移動。 -
AttackingPlayer
(プレイヤー攻撃): プレイヤーが適切な攻撃範囲内(ATTACK_RANGE_MIN
~ATTACK_RANGE_MAX
で定義)にいる場合、プレイヤーの動きを予測して爆弾設置を試みる。設置後はRepositioning
へ。 -
Repositioning
(再配置): プレイヤーに近すぎる、爆弾を設置した直後、または危険を察知した場合に、安全な位置へ移動したり、次の行動のための間合いを取ったりする。
-
-
- ステート遷移
- プレイヤーとの距離、周囲のタイル状況(ニュートラル/敵タイル)、自身のHP、爆弾のクールダウン状況などに基づいて自動で遷移。
- 能力
- HP
-
CharacterStatsSO
(敵用、LevelConfigSO
経由で設定)で定義された最大HP。 - 0になると撃破され、
LevelConfigSO
で定義されたリスポーン時間後に初期位置付近で復活。 - HP変動は
ReactiveProperty<int>
。
-
- 移動速度
-
CharacterStatsSO
(敵用)で定義された速度。
-
- 爆弾設置
-
BombStatsSO
で定義されたクールダウン時間 (enemyBaseCooldown
) を経て爆弾を設置。
-
- HP
- ビュー
-
EnemyPresenter
がアタッチされたGameObjectで表現。 -
PoolManager
によって管理(復活時はプールから再取得または再アクティブ化)。
-
2.3. タイル
- 構成
- ゲームマップはグリッド状に配置されたタイルで構成される。
- 所有権
- 各タイルは
Neutral
(中立)、Player
(プレイヤー所有)、Enemy
(敵所有) のいずれかの状態を持つ。 - この状態は
ReactiveProperty<Owner>
で管理され、変更時にTileView
が自動的に色を更新。
- 各タイルは
- 占領
- 爆弾が爆発すると、その爆風範囲内のタイルが爆弾の所有者のものになる。
2.4. 爆弾
- 性能
- 半径・信管時間
-
BombStatsSO
で定義された値に基づき生成。
-
- ダメージ
-
BombStatsSO
で定義されたダメージ量を、爆風範囲内の敵対するキャラクターに与える。
-
- 半径・信管時間
- ライフサイクル
- 設置後、一定の信管時間(
fuseTime
)が経過すると爆発。 - 爆発後は消滅し、ビューオブジェクトは
PoolManager
に返却。
- 設置後、一定の信管時間(
2.5. マップと設定
- マップ構成
-
LevelConfigSO
で定義されたタイル数 (mapTileDimensions
) とタイル間隔 (tileSpacing
) に基づいて動的に生成。 -
追加:
LevelConfigSO
に初期の敵陣地配置パターンや割合を設定可能にする(オプション)。
-
- パラメータ管理
- ゲームの主要な数値(キャラクターのHPや速度、敵の数、爆弾の性能、マップサイズ、制限時間、リスポーン時間など)は
LevelConfigSO
,CharacterStatsSO
,BombStatsSO
といったScriptableObjectによって管理。 - Unity Editorから容易に調整可能。
- これらの設定は
GameLifetimeScope
を通じて各PresenterにDI。
- ゲームの主要な数値(キャラクターのHPや速度、敵の数、爆弾の性能、マップサイズ、制限時間、リスポーン時間など)は
3. 主要システム
- DI (Dependency Injection) とライフサイクル管理 (VContainer)
-
GameLifetimeScope
がDIコンテナを構築し、ゲームに必要なオブジェクト(Model、Presenter、Service、設定SOなど)の依存関係を解決・注入。 -
IDisposable
を実装したクラスは、VContainerのスコープ終了時に自動的にDispose()
が呼び出され、リソースが解放。 -
ITickable
を実装したクラス (GameplayLoopUpdater
) は、VContainerによって毎フレームTick()
メソッドが呼び出される。
-
- イベントシステムと状態管理
- Model層の主要な状態(キャラクターのHP、タイルの所有者など)は
ReactiveProperty
で管理され、変更がリアクティブストリームとして通知される。 - キャラクターの死亡(一時的なリタイア)などの離散的なイベントは
Subject
を通じて通知される。 - Presenter層はこれらのストリームを購読し、リアクティブに変更に対応(UI更新、ロジック実行など)する。
- 購読は
CompositeDisposable
で管理され、適切に破棄される。
- Model層の主要な状態(キャラクターのHP、タイルの所有者など)は
- オブジェクトプーリング (PoolManager)
- 敵キャラクターのビューGameObjectと爆弾のビューGameObjectは、
PoolManager
によって再利用される。 - プレハブはAddressablesキー経由でロードされる。
- 敵キャラクターのビューGameObjectと爆弾のビューGameObjectは、
4. UI (ユーザーインターフェース)
- 表示 (GameUIView)
- プレイヤーのHP。
- (オプション)敵キャラクターのHP(選択ターゲットなど)。
- 現在アクティブな敵の数。
- プレイヤーと敵のタイル占有率(プログレスバー形式)。
- 残り制限時間。
- ゲーム結果メッセージ(勝利/敗北)。
- 入力 (PlayerInputView)
- キーボード(デフォルト)によるプレイヤーの移動(上下左右)と爆弾設置(スペースキー)の入力を受け付ける。
- Rxストリームとして
PlayerPresenter
に提供する。
5. ゲームフロー (高レベルな状態遷移)
-
ゲーム全体の流れは
GameFlowManager
によって管理される。 -
起動 〜 メインメニュー
-
- アプリケーション起動。
- Unityシーンがロードされ、
GameLifetimeScope
がVContainerをセットアップ。
-
-
GameFlowManager
が初期化。
- ゲーム状態が
GameState.MainMenu
に。 - メインメニューUIが表示される。
-
-
-
ゲーム開始
-
- プレイヤーがメインメニューで「スタート」ボタンをクリック。
-
-
UIButtonHook
経由でGameFlowManager.StartGame()
が呼び出される。
-
-
-
GameFlowManager
がGameOrchestratorPresenter.SetupNewGame()
を呼び出し、ゲームセッションを初期化(キャラクターのリスポーンタイマーなどもセットアップ)。
-
-
- ゲーム状態が
GameState.Playing
に遷移。
- インゲームUIが表示される。
- ゲーム状態が
-
-
ゲームプレイ中 (
GameState.Playing
)- プレイヤーはキャラクターを操作し、敵は改良されたAIに基づいて行動する(タイル占領も意識)。
- キャラクターが倒されると、一定時間後にリスポーンする。
- 爆弾の設置、爆発、タイルの占領、キャラクター間の戦闘がリアルタイムに進行する。
- プレイヤーは「ポーズ」ボタンでゲームを一時停止 (
GameState.Paused
) できる。- ポーズ中は
Time.timeScale = 0
になり、ゲームの進行が停止する。 - 再度「再開」ボタンで
Playing
状態に戻れる。
- ポーズ中は
-
ゲーム終了
- 制限時間が0になった時点で勝敗判定。
-
ゲームクリア (
GameState.GameClear
): プレイヤーのタイル占有率が敵のタイル占有率を上回っている場合。 -
ゲームオーバー (
GameState.GameOver
): 敵のタイル占有率がプレイヤーのタイル占有率を上回っている場合(または同率でプレイヤー不利とする場合など)。 - ゲームクリア/ゲームオーバーUIが表示される。
-
リトライ / メニューへ戻る
- ゲームオーバーまたはゲームクリア画面から、プレイヤーは「リトライ」または「メニューへ」を選択できる。
- 「リトライ」:
GameFlowManager.StartGame()
が再度呼ばれ、新しいゲームセッションが始まる。 - 「メニューへ」:
GameFlowManager.GoToMainMenu()
が呼ばれ、GameState.MainMenu
に戻る。
6. 詳細な制御の流れ (ゲームプレイ中)
GameOrchestratorPresenter.SetupNewGame()
)
A. ゲームセッションのセットアップ (-
- クリーンアップ: 前のセッションのデータを破棄・リセット。
-
- Model初期化:
GameState
をリセット。
- Model初期化:
-
- ワールド初期化 (
WorldInitializer
):
-
LevelConfigSO
に基づきマップ境界計算、初期タイル配置(一部敵陣地として設定可能)。 - プレイヤーの
Character
ModelとPlayerPresenter
を生成・初期化。リスポーンロジックの準備。 - 敵の
Character
Model群とEnemyPresenter
群をPoolManager
から取得して生成・初期化。リスポーンロジックの準備。
- ワールド初期化 (
-
- 状態監視Presenterの初期化:
-
PlayerStatusWatcher
がプレイヤーのHPやリスポーンを管理。 -
EnemyFleetWatcher
が敵群のHPやリスポーンを管理。
-
- UI初期表示: 制限時間、初期HP、占有率などを表示。
GameplayLoopUpdater.Tick()
)
B. フレームごとの更新 (-
- VContainerによって
GameplayLoopUpdater.Tick()
が毎フレーム呼び出される。
- VContainerによって
-
- ゲームが
Playing
状態の場合:
-
_bombController.UpdateBombTimers(Time.deltaTime)
。 -
_gameUIUpdater.UpdateOwnershipBars()
。 -
_gameUIUpdater.UpdateTimer()
(制限時間の更新と表示)。 -
_orchestrator.UpdateCharacterRespawns(Time.deltaTime)
(キャラクターリスポーン処理)。 -
_orchestrator.CheckGameResultByTimeLimit()
(制限時間による勝敗判定)。
- ゲームが
C. プレイヤーの行動と結果
-
-
PlayerInputView
が入力を検知し、PlayerPresenter
に通知 (UniRx)
-
-
-
PlayerPresenter
は移動入力に応じてPlayerModel
の位置を更新し、自身のGameObjectのtransform
も更新
-
-
-
PlayerPresenter
は爆弾設置入力(クールダウン後)に応じて_orchestrator.RequestBombSpawn()
を呼び出す
-
D. 敵AIの行動と結果
-
-
EnemyPresenter.AIBehaviorLoop()
が毎フレーム実行。
-
-
-
UpdateAIState()
:
- 周囲のタイル状況(最も近い未占領タイル、敵タイルなど)、プレイヤーの位置と状態、自身の状態(HP、爆弾クールダウン)を評価。
- 優先度に基づきステートを決定 (
CapturingTile
,ApproachingPlayer
,AttackingPlayer
,Repositioning
,Idle
)。
-
-
-
ExecuteCurrentAIState()
:
-
CapturingTile
: 目標タイルへ移動し、爆弾を設置して占領を試みる。 -
ApproachingPlayer
: プレイヤーに接近。 -
AttackingPlayer
: プレイヤーの動きを予測し(簡易的でも)、爆弾設置を試みる。 -
Repositioning
: 安全な位置へ移動。
-
-
- 移動や爆弾設置はModel更新や
_orchestrator.RequestBombSpawn()
に繋がる。
- 移動や爆弾設置はModel更新や
E. 爆弾の処理と影響
-
- キャラクターが爆弾を設置すると
_orchestrator.RequestBombSpawn()
が呼ばれ、BombController
が処理
- キャラクターが爆弾を設置すると
-
-
BombController
は Bomb Modelを生成し、PoolManager
から爆弾View(GameObject)を取得・表示
- アクティブ爆弾リストでタイマー管理
-
-
-
GameplayLoopUpdater
(内部でBombController.UpdateBombTimers
) によりタイマーが0になると、BombController
が_gameRuleServiceModel.ExplodeBomb(bombModel)
を呼び出す
-
-
GameRuleService.ExplodeBomb()
- 爆風範囲内の
TileModel.CurrentOwner
(RP) を更新- これにより、
TileView
の色が自動的に変わる
- これにより、
- 爆風範囲内の敵対する
CharacterModel.Hp
(RP) を更新(ダメージを与える)- これにより、キャラクターのHPバーUIが更新される
-
GameRuleService
がOnBombExplodedSubject
を発行
-
-
BombController
がOnBombExploded_PresenterEvent
を発行
-
-
-
GameOrchestratorPresenter
がこれらの爆発イベントを購読し、視覚・聴覚エフェクトを再生(現状はログ表示)
-
-
-
BombController
が爆弾ViewをPoolManager
に返却
-
F. キャラクターの戦闘不能とリスポーン
-
- キャラクターのHPが0になると
OnDied
Subjectが発行される。
- キャラクターのHPが0になると
-
-
PlayerStatusWatcher
/EnemyFleetWatcher
がこれを検知。
- 対応するキャラクター(のPresenterとView)を一時的に非アクティブ化(またはプールに一旦戻す)。
- リスポーンタイマーを開始(時間は
LevelConfigSO
で設定)。
-
-
-
GameOrchestratorPresenter.UpdateCharacterRespawns()
:
- 各リスポーンタイマーを更新。
- タイマーが0になったキャラクターをリスポーンさせる。
-
PoolManager
からViewを再取得(または再アクティブ化)。 - ModelのHPを最大にリセット。
- 初期位置付近の安全な場所に再配置。
- 対応するPresenterを再初期化。
-
-
GameOrchestratorPresenter.CheckGameResultByTimeLimit()
)
G. 勝敗判定のトリガー (-
-
GameplayLoopUpdater.Tick()
内から定期的に呼び出される。
-
-
-
LevelConfigSO
で設定された制限時間を監視。
-
-
- 制限時間が0になった場合:
-
_gameStateModel.GetOwnership()
でプレイヤーと敵のタイル占有率を取得。 - 占有率を比較。
- プレイヤー優位なら
GameFlowManager.Instance.Clear()
を呼び出す。 - 敵優位(または同率で不利扱いなど)なら
GameFlowManager.Instance.Over()
を呼び出す。
- プレイヤー優位なら