14. ボスの攻撃
はじめに
ボスが火の玉を吐くようにしました。5発のホーミング弾を連続して発射し、しばらく休む、を繰り返します。
現在は、プレイヤーだけが武器を持っていますが、敵も武器を持てるように修正しました。 合わせて武器周りのリファクタも進めました。
方針
- 武器にOwnerコンポーネントを追加
- コリジョン判定を適切に設定
- 武器のOwnerがプレイヤー → 弾は敵に当たる
- 武器のOwnerが敵 → 弾はプレイヤーに当たる
- Owner死亡時に武器Entityを削除
- コリジョン判定を適切に設定
- ボス用の武器(BossFireballWeapon)を追加
- BossFireballSystemを追加
- 連続して発射→休憩 を繰り返す
- ProjectileMovementSystemを追加
- ホーミング用の関数を追加
- BossFireballSystemを追加
実装
連続して発射と休憩
- stateを使って、“idle”,“firing”,“waiting"のphaseを切り替え
- 初期状態は"idle"で、後は"firing”↔“waiting"を繰り返し
- shotCooldown, waitingCooldownで弾の間隔と休憩時間をコントロール
update(_delta: number) {
for (const [weaponEntity, fireball] of ComponentManager.entries("bossFireball")) {
// 武器のオーナーが死亡している場合は処理をスキップ
const owner = ComponentManager.get("owner", weaponEntity).owner;
if (ComponentManager.has("deadFlag", owner)) continue;
const { state } = fireball;
const params = EnemyConfig.enemies.boss.weapon!;
switch (state.phase) {
case "idle":
// 発射状態を更新
state.phase = "waiting";
state.waitingCooldown = params.initialCooldown; // 初期待機時間
break;
case "firing":
state.shotCooldown -= _delta;
if (state.shotCooldown > 0) break;
// 発射間隔が終了したら、弾を発射
this.createBossFireballProjectiles(this.scene, owner);
state.remainingShots -= 1; // 残弾数を減少
state.shotCooldown = params.shotCooldown; // 発射間隔を設定
// 残弾数が0になったら、待機状態に移行
if (state.remainingShots <= 0) {
state.phase = "waiting";
state.waitingCooldown = params.waitingCooldown; // 待機時間を設定
break;
}
break;
case "waiting":
state.waitingCooldown -= _delta;
if (state.waitingCooldown > 0) break;
// 待機時間が終了したら、発射状態に戻る
state.phase = "firing";
state.remainingShots = params.numberOfShots; // 残弾数を設定
state.shotCooldown = params.shotCooldown; // 発射間隔を設定
break;
}
}
}ホーミング移動
- turnRateは1秒間に何度回転するかをラジアン単位で指定するものです。PI/2〜PI/3程度が適切です。
private calcHomingVelocity(
velocity: XY,
position: XY,
target: XY,
speed: number,
turnRate: number,
delta: number
): XY {
const desired = Math.atan2(target.y - position.y, target.x - position.x);
let angle: number | undefined = undefined;
// 初回や停止中は目標角度に設定
if (Math.abs(velocity.x) < 0.001 && Math.abs(velocity.y) < 0.001) {
angle = desired;
} else {
// 現在の角度と目標角度から新しい角度を計算
const current = Math.atan2(velocity.y, velocity.x);
const frameRotation = turnRate * (delta / 1000);
angle = Phaser.Math.Angle.RotateTo(current, desired, frameRotation);
}
// 速度を計算
return { x: Math.cos(angle) * speed, y: Math.sin(angle) * speed };
}まとめ
ボスがホーミング火の玉を吐くようになりました。
