11. 敵キャラの追加とWave処理

はじめに

現在は、スライムが定期的にスポーンして襲ってくるだけですが、敵キャラを増やして、Waveを実装したい。

方針

  • EnemyConfigで敵データを定義
  • WaveConfigでWaveデータを定義
  • WaveSystemを追加して、Waveデータにそって敵をスポーン
  • createEnemyでEnemyTypeによって異なる敵を作成

実装

EnemyConfig.ts

  • EnemyTypeで種類を定義
  • EnemyConfigで種類ごとのパラメータを定義
export type EnemyType = "slime" | "spider" | "bat";

export const EnemyConfig = {
  enemies: {
    slime: {
      maxHp: 10,
      speed: 50,
      damage: 5,
    },
    spider: {
      maxHp: 30,
      speed: 100,
      damage: 5,
    },
    bat: {
      maxHp: 20,
      speed: 120,
      damage: 8,
    },
    ...
  },
};

WaveConfig.ts

  • 各Waveの情報(長さや敵の数や種類)をWavesに定義
  • poolでは敵の種類ごとの比率をしていする
    • 直接、出現数を指定する方法もあり
import { EnemyType } from "./EnemyConfig";

export interface WaveConfig {
  duration: number | null; // 継続時間、エンドレスならnull
  total: number; // 敵の合計数
  spawnInterval: number; // 出現間隔
  pool: Partial<Record<EnemyType, number>>; // 敵の種類と出現確率(合計100)
}

export const Waves: WaveConfig[] = [
  { duration: 30_000, total: 30, spawnInterval: 1_000, pool: { slime: 100 } },
  { duration: 40_000, total: 50, spawnInterval: 800, pool: { slime: 70, spider: 30 } },
  ...
  { duration: null, total: 9999, spawnInterval: 500, pool: { bat: 100 } },
];

WaveSystem.ts

  • updateで現在のwaveに応じて敵をスポーン
  • wave情報に応じて、どの敵をスポーンするか決定
    • 今回はpoolの比率に応じてランダムで決めている
    • 別の方法として、wave開始時に長さtotalの配列を作ってしまうのもあり

コードは長くなるので概要のみ

export class WaveSystem {
  ...
  private waveIndex = 0;
  private elapsed = 0; // Wave経過時間
  private spawnTimer = 0; // 次のスポーンまでのタイマー
  private spawned = 0; // スポーンした数

  update(delta: number) {
    const wave = Waves[this.waveIndex];
    if (!wave) return; // すべてのWaveが終了

    // elapsed,spawnTimerを更新
    ...

    // spawnIntervalが経過するごとに敵をスポーン (spawnEnemyByPoolを呼ぶ)
    ...

    // 設定時間が経過、または敵の全滅をチェックしてWaveを終了 (nextWaveを呼ぶ)
    ...
  }

  private spawnEnemyByPool() {
    // プールから敵をランダムに選択
    ...

    // プレイヤーの周囲200~400pxランダムに出現
    ...

    createEnemy(this.scene, x, y, playerEntity, type);
  }

  private nextWave() {
    // 次のWaveの準備
    ...
    // イベント発火
    this.scene.game.events.emit("WaveStarted", { waveIndex: this.waveIndex });
  }
}

createEnemy.ts

敵の生成を行います。spriteとanimationのkeyを切り替えるだけです。

export function createEnemy(
  scene: Phaser.Scene,
  x: number,
  y: number,
  targetEntity: Entity,
  enemyType: EnemyType
): number {
  const entity = EntityManager.createEntity();

  // enemyTypeごとのスプライト設定
  const sprite = scene.physics.add.sprite(x, y, enemyType);
  sprite.play(enemyType);
  sprite.setOrigin(0.5);
  sprite.setCollideWorldBounds(true);

  // enemyTypeごとのパラメータ設定
  const { maxHp, speed, damage } = EnemyConfig.enemies[enemyType];
  ComponentManager.set("enemyTag", entity, {});
  ComponentManager.set("sprite", entity, { sprite });
  ComponentManager.set("target", entity, { targetEntity });
  ComponentManager.set("health", entity, { hp: maxHp, maxHp });
  ComponentManager.set("speed", entity, { speed });
  ComponentManager.set("damage", entity, { damage });

  // イベント発火
  scene.game.events.emit("EnemyCreated", { entity, sprite });
}

まとめ

これだけで複数の敵が襲ってくるようになりました。次は敵ごとに移動パターンを変えます。