モンスターとの当たり判定
今回はモンスターとの当たり判定を考えることにします。
ダメなコード
パックマンとモンスターが同じセルにいる場合はモンスターに捕まったと考えればよいと考え、このようなコードを書いてみました。
タイマーの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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
public partial class Form1 : Form { private async void Timer_Tick(object sender, EventArgs e) { if(ignoreTick) return; ignoreTick = true; Task<bool> pacTask = MovePacmanDirect(pacDirect); Task<bool> monstarsTask = MoveMonstars(); await pacTask; await monstarsTask; ignoreTick = false; if(IsStageClear) { timer.Stop(); MessageBox.Show("ゲームクリア!"); } if(GetPacCellMonster() >= 0) { timer.Stop(); MessageBox.Show("やられた!"); timer.Start(); } } // パックマンと同じセルにいるモンスターを返す int GetPacCellMonster() { for(int i=0; i<4; i++) { if(pacCellX == MonstersCellX[i] && pacCellY == MonstersCellY[i]) return i; } return -1; } } |
すれ違いも当たり判定とするには
すれ違いも捕まったことにする場合はどうすればいいのでしょうか?
「隣り合っている場合で、両者が接近する方向に移動しようとしている場合も当たりと判定する」というのはどうでしょうか?
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 66 67 68 69 70 |
public partial class Form1 : Form { private async void Timer_Tick(object sender, EventArgs e) { if(ignoreTick) return; ignoreTick = true; Task<bool> pacTask = MovePacmanDirect(pacDirect); Task<bool> monstarsTask = MoveMonstars(); await pacTask; await monstarsTask; ignoreTick = false; if(IsStageClear) { timer.Stop(); MessageBox.Show("ゲームクリア!"); } if(IsPacmanDead() >= 0) { timer.Stop(); MessageBox.Show("やられた!"); timer.Start(); } } int IsPacmanDead() { for(int i=0; i<4; i++) { if(pacCellX == MonstersCellX[i] && pacCellY == MonstersCellY[i]) return i; if(pacCellX == MonstersCellX[i] + 1 && pacCellY == MonstersCellY[i]) { if(pacDirect == Direct.West && MonstersDirect[i] == Direct.East) return i; else continue; } if(pacCellX == MonstersCellX[i] - 1 && pacCellY == MonstersCellY[i]) { if(pacDirect == Direct.East && MonstersDirect[i] == Direct.West) return i; else continue; } if(pacCellX == MonstersCellX[i] && pacCellY == MonstersCellY[i] + 1) { if(pacDirect == Direct.North && MonstersDirect[i] == Direct.South) return i; else continue; } if(pacCellX == MonstersCellX[i] && pacCellY == MonstersCellY[i] - 1) { if(pacDirect == Direct.South && MonstersDirect[i] == Direct.North) return i; else continue; } } return -1; } } |
ゲームオーバーの処理
最後にゲームオーバーの処理をつくります。
ゲームスタート時の残機は「3」とし、モンスターにつかまるたびに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 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 66 67 |
public partial class Form1 : Form { const int PAC_REST_COUNT = 3; int pacRest = PAC_REST_COUNT; async Task<bool> OnPacmanDead() { // モンスターに捕まったら残機を減らす pacRest--; // 0になったらゲームオーバーの処理をさせるためにfalseを返す if(pacRest <= 0) return false; // 捕まったときはいったん動作を止める await Task.Delay(1000); // パックマンとモンスターをもとの位置に戻す InitPacman(); InitMonstar(0); InitMonstar(1); InitMonstar(2); InitMonstar(3); Invalidate(); // いったん動作を止める await Task.Delay(1000); return true; } private async void Timer_Tick(object sender, EventArgs e) { if(ignoreTick) return; ignoreTick = true; Task<bool> pacTask = MovePacmanDirect(pacDirect); Task<bool> monstarsTask = MoveMonstars(); await pacTask; await monstarsTask; // ゲームオーバー判定のフラグ bool isGameOver = false; // モンスターに捕まったかどうか if(IsPacmanDead() >= 0) isGameOver = ! await OnPacmanDead(); // OnPacmanDead()がfalseを返せばゲームオーバー ignoreTick = false; // ステージクリア、ゲームオーバー時はその旨表示する if(IsStageClear) { timer.Stop(); MessageBox.Show("ステージクリア!"); } if(isGameOver) { timer.Stop(); MessageBox.Show("GameOver"); } } } |
パックマンはパワー餌を食べると、その効果が持続しているあいだはモンスターを食べることができます。この処理は次回にすることに・・・。