ボス敵にはザコ敵にはないボスらしい攻撃をさせたいものです。そこでボスからは放射状に大量の弾丸を発射させます。それからプログラミング講座 第33回【シューティングゲーム作成(13)弾幕/JavaScript】と同じようにボスが放つ弾丸は最初はボスの周りに出現して、それから周囲に広がっていくようにします。
ボスが放つ弾丸はザコ敵のものとは動き方が違うので、EnemyBurretクラスを継承してBossEnemyBurretクラスをつくります。IsMoveフラグをつくって最初は動かないようにします。また同時に弾丸を発生させるのではなく、Boss.Move()が実行されるたびにひとつずつ発生してボスの周囲が弾丸で囲まれてから動き出す仕様にします。
ボスが放つ弾丸は最初はボスの周りに配置されるだけで動かないように見えます。しかし実際にはY座標はForm1.Speedだけ増えています。それからボスは左右に移動しているので止まっている弾丸もボスにあわせて左右に動かないとおかしな描画になってしまいます。
そこでBossEnemyBurretのコンストラクタにボス自身をわたすことにします。そして前回のボスのX座標との差分をもとめて弾丸を移動させます。
ボスの周囲が完全に弾丸で囲まれたらIsMoveフラグをtrueにします。あとは通常の敵弾と同じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
public class BossEnemyBurret : EnemyBurret { public bool IsMove = false; Boss _boss = null; float oldBossX = 0; public BossEnemyBurret(Boss boss, float x, float y, float vecX, float vecY) : base(x, y, vecX, vecY) { _boss = boss; oldBossX = boss.X; } // すぐに動き出さないようにする。 // ただし止まっているように見せかけるために移動の処理は必要 public override void Move() { if(IsMove) { X += VecX; Y += VecY; } else { // 前回のボスのX座標との差分 float difBossX = _boss.X - oldBossX; Y += Form1.Speed; X = X + difBossX; oldBossX = _boss.X; } MoveCount++; } } |
ではBossクラスに弾丸を発射する機能を追加しましょう。弾丸をつくったらフィールド変数bossEnemyBurretsのなかにも格納します。そしてボスの周囲が弾丸で囲まれたらBossEnemyBurret.IsMoveを trueに変更します。すると弾丸が放射状に飛び始めます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
public class Boss : EnemyBase { List<BossEnemyBurret> bossEnemyBurrets = new List<BossEnemyBurret>(); void Shot() { int i = MoveCount % 31; if(i != 30) { int angle = i * 12 + 180; double rad = angle * Math.PI / 180; float vecX = (float)Math.Cos(rad) * 0.2f; float vecY = (float)Math.Sin(rad) * 0.2f + Form1.Speed; float x = X + (float)Math.Cos(rad) * 3; float y = Y + (float)Math.Sin(rad) * 3; BossEnemyBurret burret = new BossEnemyBurret(this, x, y, vecX, vecY); Form1.EnemyBurrets.Add(burret); bossEnemyBurrets.Add(burret); } if(i == 30) { foreach(BossEnemyBurret burret in bossEnemyBurrets) burret.IsMove = true; bossEnemyBurrets.Clear(); } } } |
それからプログラミング講座 第33回【シューティングゲーム作成(13)弾幕/JavaScript】ではボスのHPが低下すると攻撃パターンを変えています。そこで同じように変えてみましょう。
Lifeが半分以下になったらザコ敵をつくって参戦させます。これをForm1.Enemiesに格納すればよさそうなのですが、Form1.MoveEnemies()メソッドのなかでforeach文が実行されているときにEnemiesの要素を変更すると例外が発生します。そこでいったんBossクラスのなかで新しく作成したボス支援ザコキャラを保存しておき、foreach文が実行されていないときにForm1.Enemiesに追加します。
まずどんな敵を参戦させるかですが、サングラスをした小型のやさぐれひよこにするのがいいかなと思ったのですが、じっさいに試してみるとイマイチだったので黄色のひよこを使います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
public class Boss : EnemyBase { public List<EnemyBase> Enemies = new List<EnemyBase>(); public override void Move() { if(Z > 2) { Z -= 0.1f; Y += Form1.Speed; return; } IsReady = true; Z = 2f; if(X > 3) isRight = false; if(X < -3) isRight = true; if(isRight) VecX = 0.1f; else VecX = -0.1f; // 周囲に弾幕を張るように弾丸を配置 Shot(); // Lifeが半分以下になったらザコ敵をつくって参戦させる。 // Lifeが4分の1以下になったらさらに参戦する数を増やす if(Life > 0 && Life < MaxLife / 4 && MoveCount % 16 == 0) { int r = Random.Next(800); float x = 1f * r / 100 - 4f; Enemies.Add(new Enemy2(x, Y + 20, 0.5f, 1, 10)); } else if(Life > 0 && Life < MaxLife / 2 && MoveCount % 64 == 0) { int r = Random.Next(800); float x = 1f * r / 100 - 4f; Enemies.Add(new Enemy2(x, Y + 20, 0.5f, 1, 10)); } base.Move(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public partial class Form1 : Form { void MoveEnemies() { // 対ボス戦のとき子分の敵キャラが存在するなら追加する EnemyBase boss = Enemies.FirstOrDefault(x => x is Boss); if(boss != null) { Enemies.AddRange(((Boss)boss).Enemies); (((Boss)boss).Enemies).Clear(); } foreach(EnemyBase enemy in Enemies) { enemy.Move(); } } } |
そしてボスが死ぬときはザコ敵にもいっしょに死んでもらいましょう。「死なばもろとも」です。
それからボスが死んでもボスが発した弾丸は飛び続けてもよいと思いますが、飛ぶ前の状態の弾丸が環状になってしばらく残るのはちょっと見た目的におかしいので、これも消えてもらいます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
public partial class Form1 : Form { void OnBossDead(Boss boss) { // ボスを倒した場合はボスを大爆発させる BigExplosionWhenBossDead(boss.X, boss.Y); // 子分も一緒に死んでもらう foreach(EnemyBase enemy in Enemies) { if(!enemy.IsDead) { enemy.IsDead = true; BigExplosion(enemy.X, enemy.Y); } } // 飛ぶまえのボスの弾丸も消す foreach(EnemyBurret enemyBurret in EnemyBurrets) { if(enemyBurret is BossEnemyBurret) { BossEnemyBurret burret = (BossEnemyBurret)enemyBurret; if(!burret.IsMove) burret.IsDead = true; } } // 少し時間をおいてからボス死亡後の処理をおこなう Timer timer = new Timer(); timer.Interval = 2000; timer.Tick += Timer_Tick1; timer.Start(); void Timer_Tick1(object sender, EventArgs e) { Timer t = (Timer)sender; t.Stop(); t.Dispose(); AfterBossDead(); } } } |