はじめに
ゲームの進行を「Wave」単位で管理できるようにし、Waveごとにボス戦を発生させる仕組みを実装しました。
スマホで遊ぶことを想定し、ゲーム開始時は画面をタッチすることでゲームを開始できるようにし、ゲームオーバーになった場合も画面をタッチすることで再開できるように操作フローを整備しました。
UIについても手を入れ、左上にWave数の表示と、右下にWaveごとの撃破ノルマ数の表示を追加しました。プレイヤーが「あと何体倒せば次に進むのか」を把握できるようになり、進行状況がより見えるようになったと思います。
ここまでの成果
ゲームの仕様
新しく追加した仕様には「(新)」を付けています。
変更した仕様に「(変)」を付けています。
- (変)プレイヤーHP設定:初期HPは3
- プレイヤーHP表示:画面右上に「HP:X」を常時表示する
- (新)ゲーム開始:開始前に「TOUCH TO START」を表示し、タップで開始
- (変)敗北条件:HPが0になった時点で「GAME OVER TOUCH TO RESTART」UIを表示し、ゲームを停止する
- 操作方法:WASDキー または 十字キー(上下左右の移動)
- 攻撃方法:自動発射
- (変)敵キャラ実装数:100体(e001~e100)
- (新)ボス実装数:9体(b001~b009)
- (変)敵キャラHP
- e001〜e010:HP 1 , e011〜e020:HP 10 , e021〜e030:HP 15 , e031〜e040:HP 20 , e041〜e050:HP 25 ,,,
- 敵キャラの移動:画面上から下方向に直進する
- スコア:画面左上に「Score:X」を常時表示する
- e001~e010:100点 , e011~e021:200点 , e021~e031:300点 ,,,
- ステータス:アイテムの取得で強化できるステータス
- 弾速:上限無し
- 連射速度:発射間隔は0.05秒未満にならないように設計
- 攻撃力:上限無し
- (変)移動速度:最大10(初期2から上昇、上限あり)
- (変)HP:上限は3
- アイテム一覧
- (新)Waveの仕様
- Waveは 1~100 まで進行
- Waveは基本的に以下の流れ
- 敵キャラが湧くフェーズ
- Waveボスが湧くフェーズ
- Waveボスを倒すと次のWaveに進む
- 各Waveの撃破ノルマ:10+Wave×2(最大で100)
- 敵キャラの同時出現上限:5+(Wave/10)
- 各Waveの敵キャラの出現ID範囲:(Wave-5)~Wave
- Wave30の場合、敵キャラの出現IDの範囲は25~30
- 通常ボスを設定
- Wave番号の敵キャラが通常ボス
- 通常ボスのサイズ:敵キャラの2倍
- 通常ボスのHP:敵キャラHP×Wave数×5
- 特別ボスを設定
- 出現するWave:5の倍数のWave
- 出現するボスID:b00{Wave/5}
- ボスの基本HP:1
- ボスのHP倍率:基本HP×ボス回数×10
Waveの実装
Waveの進行(敵キャラ→ボス→クリア)を実現するための実装を解説します。
Wave開始時に「そのWaveの難易度パラメータ」を計算して固定
Waveは、開始時点で「ノルマ」「同時出現上限」「敵IDの出現範囲」を決めておきます。途中で条件がブレず、調整もしやすくなります。
<Waveパラメータを作る主要素>
| カテゴリ | コード内の要素 | 役割 |
| 現在Wave | currentWave | 何Wave目かを保持 |
| フェーズ | WavePhase | 敵キャラ/ボス/クリアを判定 |
| ノルマ | killQuota | 敵キャラフェーズで倒すべき数 |
| 上限 | spawnLimit | 同時に存在できる敵キャラ数の上限 |
| ID範囲 | minEnemyId,maxEnemyId | そのWaveで出現する敵IDの範囲 |
| 上限キャップ | MAX_QUOTA_LIMIT,MAX_WAVE | ノルマやWave上限の安全装置 |
<実際のコード>
StartWave() でWaveの初期化を行い、CalculateWaveParameters() で難易度パラメータを確定します。
public enum WavePhase
{
ZakoPhase,
ClimaxPhase,
WaveClear
}
private const int MAX_WAVE = 100;
private const int MAX_QUOTA_LIMIT = 100;
public void StartWave(int wave)
{
currentWave = wave;
currentKillCount = 0;
currentPhase = WavePhase.ZakoPhase;
CalculateWaveParameters();
UpdateUI();
// Wave開始演出
if (GameUIManager.Instance != null)
{
string msg = (currentWave % 5 == 0) ? "BOSS WAVE!" : $"WAVE {currentWave}";
GameUIManager.Instance.ShowPhaseMessage(msg, 2.0f);
}
Debug.Log($"Wave {currentWave} (Loop {loopCount}) Started! Quota: {killQuota}");
}
private void CalculateWaveParameters()
{
// 1. ノルマ計算
int calculatedQuota = 10 + (currentWave * 2);
killQuota = Mathf.Min(calculatedQuota, MAX_QUOTA_LIMIT);
// 2. 出現数上限
spawnLimit = 5 + (currentWave / 10);
// 3. 出現ID範囲
maxEnemyId = currentWave;
minEnemyId = Mathf.Max(1, currentWave - 5);
// IDが100を超えないようにキャップ(データ数依存)
if (maxEnemyId > 100) maxEnemyId = 100;
if (minEnemyId > 100) minEnemyId = 100;
}
private void UpdateUI()
{
if (GameUIManager.Instance != null)
{
GameUIManager.Instance.UpdateWaveText(currentWave + (loopCount * 100)); // 通算Wave数を表示
GameUIManager.Instance.UpdateQuotaText(currentKillCount, killQuota);
}
}
撃破ノルマ到達でボス戦へ移行
Wave進行は「敵キャラを一定数倒したらボス戦へ」という構造にしています。
<フェーズ移行を作る要素>
| カテゴリ | コード内の要素 | 役割 |
| キル加算 | AddZakoKill() | 敵キャラ撃破時にカウント増加 |
| ノルマ判定 | currentKillCount >= killQuota | ボス戦移行条件 |
| フェーズ移行 | EnterClimaxPhase() | WARNING/RUSH表示とClimaxPhase切替 |
| スポーン分岐 | wm.Phase == … | 敵キャラ or ボス戦生成を切替 |
| 1回だけ生成 | climaxSpawned | クライマックス対象を多重生成しない |
<実際のコード>
撃破ノルマ到達によるボス戦へ移行する箇所は下記となります。
public void AddZakoKill()
{
if (currentPhase != WavePhase.ZakoPhase) return;
currentKillCount++;
UpdateUI();
if (currentKillCount >= killQuota)
{
EnterClimaxPhase();
}
}
private void EnterClimaxPhase()
{
currentPhase = WavePhase.ClimaxPhase;
// 警告演出
if (GameUIManager.Instance != null)
{
bool isBoss = (currentWave % 5 == 0);
string msg = isBoss ? "<color=red>WARNING\nBOSS APPROACHING" : "<color=yellow>RUSH TIME!";
GameUIManager.Instance.ShowPhaseMessage(msg, 3.0f);
}
}
敵キャラフェーズとボス戦フェーズの動作を切り替える箇所は下記となります。
// Assets/Scripts/EnemySpawner.cs
private IEnumerator SpawnRoutine()
{
// WaveManagerの準備待ち
while (WaveManager.Instance == null) yield return null;
while (true)
{
activeEnemies.RemoveAll(item => item == null);
var wm = WaveManager.Instance;
// フェーズによる分岐
if (wm.Phase == WaveManager.WavePhase.ZakoPhase)
{
climaxSpawned = false; // 次のボス戦に備えてリセット
// 上限チェック
if (activeEnemies.Count < wm.SpawnLimit)
{
SpawnZako(wm);
}
}
else if (wm.Phase == WaveManager.WavePhase.ClimaxPhase)
{
// ボス/Rushターゲットを1回だけ出す
if (!climaxSpawned)
{
SpawnClimaxTarget(wm.CurrentWave);
climaxSpawned = true;
}
// 補足: Rush時の「背景雑魚」演出を入れるならここに記述
}
float interval = 2.0f;
if (ConfigManager.Instance != null)
{
interval = ConfigManager.Instance.Config.spawnInterval;
}
yield return new WaitForSeconds(interval);
}
}
private void SpawnZako(WaveManager wm)
{
// 出現ID範囲からランダムに決定
int randomId = Random.Range(wm.MinEnemyId, wm.MaxEnemyId + 1);
if (zakoMap.TryGetValue(randomId, out EnemyData data))
{
CreateEnemyObject(data, enemyBasePrefab, false);
}
}
以上です。
コメント