爆弾を投下する
スクランブルもどきでは自機から発射された弾丸は水平に同じ速度で移動していきますが、投下された爆弾はそのまま下へ移動するのではなく、最初前方へ移動してそれから下に向けて落下していきます。そこでBombクラスを作成して爆弾の動きを管理します。
Bombクラス
プロパティ
X,Y
爆弾が表示される座標です。
Size
爆弾の表示サイズです。
IsDead
最初は falseです。爆弾がなにかに命中したのであればtrueになります。
DeadRectangle
この矩形内にある場合、爆弾と衝突したことになります。
Tick
その爆弾が投下されて何回Tickイベントが発生したかを記憶させます。これによって投下後の動きがかわります。
メソッド
void Fall()
爆弾を落下させます。投下されてからの時間で動きが異なります。
実際のコード
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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
public class Bomb { public Bomb(int x, int y) { X = x; Y = y; } public int X { get; set; } public int Y { get; set; } public Size Size { get { return new Size(4, 4); } } public bool IsDead { get; set; } = false; public Rectangle DeadRectangle { get { return new Rectangle(X, Y, Size.Width, Size.Height); } } public int Tick { get; set; } = 0; public void Fall() { Tick++; // X軸 if(Tick < 10) X += 5; // Y軸 if(Tick > 10) Y += 5; else if(Tick > 5) Y += 2; } } |
次にForm1クラスで爆弾を投下するメソッドを作成します。
Xキーを押すとJiki.Bombメソッドが呼び出されて爆弾の投下が行なわれます。Jiki.BombメソッドではList
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public partial class Form1 : Form { List<Bomb> bombList = new List<Bomb>(); private void Form1_KeyDown(object sender, KeyEventArgs e) { if(e.KeyCode == Keys.Up) JikiDirect = JikiDirect.Up; if(e.KeyCode == Keys.Down) JikiDirect = JikiDirect.Down; if(e.KeyCode == Keys.Left) JikiDirect = JikiDirect.Left; if(e.KeyCode == Keys.Right) JikiDirect = JikiDirect.Right; if(e.KeyCode == Keys.Z) Jiki.Shot(bulletList); if(e.KeyCode == Keys.X) Jiki.Bomb(bombList); } } |
これがJikiクラスのBombメソッドです。自機の死亡判定がされる矩形の相対位置で爆弾の出現位置を決めています。
1 2 3 4 5 6 7 |
public class Jiki { public void Bomb(List<Bomb> bombList) { bombList.Add(new Bomb(DeadRectangle.Left + 10, DeadRectangle.Bottom + 2)); } } |
Form1クラスのなかでTickイベントが発生したら爆弾を下に移動させて当たり判定を行ないます。
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 |
public partial class Form1 : Form { private void Timer_Tick(object sender, EventArgs e) { TimerTick_JikiMove(); panelEx1.Invalidate(); // 弾丸を前へ移動させる bulletList = bulletList.Select(x => new Point(x.X + 10, x.Y)).ToList(); // 命中した弾丸は取り除く RemoveBalletsHitTopography(); RemoveBalletsOutOfField(); RemoveBalletsHitMissile(); foreach(Bomb bomb in bombList) bomb.Fall(); // なにかに命中した爆弾は取り除く RemoveBombsHitTopography(); RemoveBombsOutOfField(); RemoveBombsHitMissile(); CheckIsDead(); } } |
まず地面に命中した爆弾や画面の外にでた爆弾は表示する必要がないのでリストから取り除いています。敵のミサイルに命中した爆弾もリストから取り除いていますが、点数計算のためにイベントを発生させています。
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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
public partial class Form1 : Form { // フィールドの外へ出た爆弾はリストから除外する void RemoveBombsOutOfField() { bombList = bombList.Where(x => x.X < panelEx1.Width).ToList(); bombList = bombList.Where(x => x.Y < panelEx1.Height).ToList(); } // 地面に当たった爆弾はリストから除外する void RemoveBombsHitTopography() { Rectangle[] rects = Topography.GetRectangles(); foreach(Bomb bomb in bombList) { Rectangle rect1 = bomb.DeadRectangle; foreach(Rectangle rect2 in rects) { if(rect1.Bottom < rect2.Top || rect1.Top > rect2.Bottom || rect1.Right < rect2.Left || rect1.Left > rect2.Right) { // 自機から投下された爆弾は命中していない } else { bomb.IsDead = true; break; } } } bombList = bombList.Where(x => !x.IsDead).ToList(); } // 敵ミサイルに命中した爆弾はリストから除外するとともに // イベントを発生させる。 void RemoveBombsHitMissile() { foreach(Missile missile in Missiles) { if(missile.IsDead) continue; foreach(Bomb bomb in bombList) { Rectangle rect = bomb.DeadRectangle; if(rect.Bottom < missile.DeadRectangle.Top || rect.Top > missile.DeadRectangle.Bottom || rect.Right < missile.DeadRectangle.Left || rect.Left > missile.DeadRectangle.Right) { // 自機から放たれた弾丸は命中していない } else { missile.IsDead = true; bomb.IsDead = true; HitMissile?.Invoke(this, new EventArgs()); break; } } } bombList = bombList.Where(x => !x.IsDead).ToList(); } } |
あとはイベントハンドラPanelEx1_Paintに描画時の処理を書き加えます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public partial class Form1 : Form { private void PanelEx1_Paint(object sender, PaintEventArgs e) { Jiki.Show(e.Graphics); Topography.Show(e.Graphics); // ミサイルの描画 foreach(Missile missile in Missiles) missile.Draw(e.Graphics); // 自機弾丸の描画 foreach(Point pt in bulletList) e.Graphics.FillRectangle(new SolidBrush(Color.White), new Rectangle(new Point(pt.X, pt.Y), GetBulletSize())); // 自機から投下された爆弾の描画 foreach(Bomb bomb in bombList) e.Graphics.FillRectangle(new SolidBrush(Color.Orange), new Rectangle(new Point(bomb.X, bomb.Y), bomb.Size)); } } |
ミス判定
最後に自機が敵に接触したらミスとなるので、これも実装することにします。
1 2 3 4 5 6 7 8 9 10 11 12 |
public partial class Form1 : Form { void CheckIsDead() { bool isDead = Jiki.IsDead(Topography.GetRectangles()); if(isDead) { Timer.Stop(); MessageBox.Show("Miss"); } } } |