C# OpenTKでスクランブルのようなゲームを作成しています。
今回はミス時の処理を行ないます。ミスとは敵(地面も含む)に衝突してしまった、燃料切れで墜落してしまったなどが考えられます。自機や敵にはこれより内部に他のオブジェクトが存在する場合は衝突しているという矩形が設定されているので、これを使えば簡単にできそうです。
JikiクラスのDeadRectangleFメソッドは、自機の当たり判定をするために必要な矩形を取得するためのメソッドです。
1 2 3 4 5 6 7 8 9 |
public class Jiki { public RectangleF DeadRectangleF() { //return new RectangleF(new PointF(X - 1, Y - 0.5f), new SizeF(2, 1)); // ↑ サイズ的にはこうなるが、当たり判定が厳しすぎるので緩めにする return new RectangleF(new PointF(X - 0.8f, Y - 0.4f), new SizeF(1.6f, 0.8f)); } } |
StageクラスのIsDead()メソッドは自機が敵と衝突したかどうかを判定するためのメソッドです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class Stage { // 自機が敵に接触しているなら死亡と判定する bool IsDead() { RectangleF deadRect = Jiki.DeadRectangleF(); // 地面に接触または墜落 List<Block> blocks = GetTestBlocks(); if(blocks.Any(x => IsPiledRectange(deadRect, x.DeadRectangleF()))) return true; // ミサイルに接触 if(Missiles.Any(x => !x.isDead && IsPiledRectange(deadRect, x.DeadRectangleF()))) return true; // 燃料タンクに接触 if(FuelTanks.Any(x => !x.isDead && IsPiledRectange(deadRect, x.DeadRectangleF()))) return true; return false; } } |
StageクラスのUpdate()内で自機が敵と接触したかどうかを調べます。そして敵に接触した場合は自機を消滅させます(爆発するような処理をさせたいがあとで考えます)。それと同時に自機死亡のイベントを発生させます。
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 class Stage { public event EventHandler JikiDead; public void Update() { UpdateJikiPosition(); UpdateMissilesPosition(); // 敵に接触した場合は自機を消滅させる if(IsDead()) { Jiki.IsShow = false; // 自機死亡のイベントを発生させる JikiDead?.Invoke(this, new EventArgs()); return; } UpdateBulletsPosition(); GetObjectsHitBullet(); UpdateBombsPosition(); GetObjectsHitBomb(); } } |
Form1クラスで自機死亡のイベントを捕捉したらMiss()メソッドを呼び出します。Miss()メソッドでは残機を1減らし(0であるならゲームオーバー)、再出発地点に自機と視界を移動させます。
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 |
public partial class Form1 : Form { public Form1() { InitializeComponent(); FormSizeFix(); timer.Tick += Timer_Tick; TimerIntervalReset(); this.BackColor = Color.Black; InitScore(); InitFuel(); // 自機死亡のイベントを処理できるようにする Stage.JikiDead += Stage_JikiDead; } // 自機が死亡したらMiss()メソッドを呼び出す。 private void Stage_JikiDead(object sender, EventArgs e) { Miss(); } void Miss() { // ミスをしたらタイマーを止める timer.Stop(); // 残機を1減らし、0であるならゲームオーバー Rest--; if(Rest == 0) { GameOver(); return; } // 再出発地点に自機を移動させる処理(後述) } } |
これは残機を表示させるためのメソッドです。
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 |
public partial class Form1 : Form { const int RestMax = 3; int _rest = RestMax; int Rest { get {return _rest; } set { labelRest.BackColor = Color.Black; labelRest.ForeColor = Color.White; _rest = value; labelRest.Text = "残 " + _rest; } } void GameStart() { TimerIntervalReset(); EyeX = 0; EyeY = 0; Stage.Jiki.IsShow = true; Stage.Jiki.SetStartPosition(); Fuel = FuelMax; CurDirect = Direct.None; // ゲームスタート時にはRestプロパティをRestMaxに設定する Rest = RestMax; Stage.Init(); timer.Start(); } } |
ではMiss()メソッドはどのように書けばいいのでしょうか?
まずタイマーを止めます。そして残機を1減らし、残機0ならゲームオーバー、そうでないときは一定時間経過後、ゲーム再開となります。再開するために別のタイマーを作成して3秒後にリスタートさせます。
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 partial class Form1 : Form { void Miss() { // ミスをしたらタイマーを止める timer.Stop(); // 残機を1減らし、0であるならゲームオーバー Rest--; if(Rest == 0) { GameOver(); return; } // 一定期間経過後、ゲーム再開 Timer restartTimer = new Timer(); restartTimer.Interval = 3000; restartTimer.Tick += RestartTimer_Tick; restartTimer.Start(); } private void RestartTimer_Tick(object sender, EventArgs e) { Timer t = (Timer)sender; t.Stop(); t.Dispose(); Restart(); } } |
ではRestart()メソッドはどのように書けばいいのでしょうか? スクランブルにはステージが存在し、ミスをするとステージの最初にもどってやり直しとなる仕様になっています。そのため最初のステージはクリアしたあと、第二ステージの途中でミスをした場合は第二ステージの最初からやり直すことになります。
いまは第一ステージしか作成していません(第二ステージではUFO、第三ステージではファイアボールが飛んでくるのですが・・・)。
いまは第一ステージしか存在しないので最初に戻ります。そのためRestart()メソッドの内容は以下のようなものになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public partial class Form1 : Form { void Restart() { EyeX = 0; EyeY = 0; Stage.Jiki.IsShow = true; Stage.Jiki.SetStartPosition(); CurDirect = Direct.None; Fuel = FuelMax; // 敵の位置も元に戻さないといけないので必要 Stage.Init(); timer.Start(); } } |
また残機がゼロになった場合はゲームオーバーです。ここでは単純に「ゲームオーバー」と書かれたメッセージボックスを表示するだけにします。
ただこれではglControl1.Refresh()の前にGameOver()メソッドが先に呼び出されると自機が表示されたままになるので(実際に試してみるとそのようになる)glControl1.Refresh()を先に実行しておきます。
1 2 3 4 5 6 7 8 |
public partial class Form1 : Form { void GameOver() { glControl1.Refresh(); MessageBox.Show("ゲームオーバー"); } } |