シューティングゲームの作成⑤(ボス戦の追加)

C#

はじめに

ゲームの進行を「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
  • アイテム一覧
アイテム名 画像 効果 上限
青い札 取得ごとに弾の速度加算 上限無し
手裏剣 発射間隔を短縮 0.05秒未満にならない
赤い勾玉 ダメージ量をアップ 上限無し
韋駄天草履 取得ごとにプレイヤーの速度加算 最大10
プレイヤーのHPを1回復 最大3
直進弾 前方に直進する弾
Lv1:単発
Lv2:2連装
Lv3:3連装
Lv4:4連装
Lv5:5連装
レベル5
放射弾 扇状に広がる弾
Lv1:3方向
Lv2:3方向、攻撃範囲を拡大
Lv3:5方向
Lv4:5方向、弾サイズをアップ
Lv5:7方向
レベル5
誘導弾 最も近い敵を追尾する弾
Lv1:単発
Lv2:2発同時
Lv3:3発同時
Lv4:4発同時
Lv5:5発同時
レベル5
  • (新)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);
    }
}

以上です。

コメント

タイトルとURLをコピーしました