今回でギャラクシアンもどきを完成させます。完成品はこんな感じです。
当たり判定
当たり判定、Timer1_Tick メソッドの最後の部分です。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
private void Timer1_Tick(object sender, EventArgs e) { countTimerTick++; SpaceshipMove(); BulletsMove(); StandingByEnemysMove(); BeginAttack(); AttackEnemiesMove(); EnemyManager.EnemyBulletsMove(); Judge(); // ココ } |
自機の弾丸と敵、敵と自機、敵弾丸と自機の当たり判定をおこないます。そのためのメソッドです。どちらかというとハズレ判定のような中身になってしまいました。
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 |
bool HitJude(PictureBox a, PictureBox b) { if (a.Left > b.Right) return false; if (a.Right < b.Left) return false; if (a.Top > b.Bottom) return false; if (a.Bottom < b.Top) return false; return true; } void Judge() { // 自機の弾丸と敵の当たり判定 foreach (var bullet in Bullets) { List<Enemy> enemies = EnemyManager.Enemies; foreach (var enemy in enemies) { if(HitJude(bullet, enemy)) { OnHitEnemy(bullet, enemy); break; } } } // 敵と自機の当たり判定 foreach (var enemy in EnemyManager.Enemies) { if (HitJude(pictureBoxSpaceship, enemy)) { enemy.Dispose(); OnMiss(); break; } } // 敵弾丸と自機の当たり判定 foreach (var enemyBullet in EnemyManager.EnemyBullets) { if (HitJude(pictureBoxSpaceship, enemyBullet)) { enemyBullet.Dispose(); OnMiss(); break; } } if (EnemyManager.Enemies.Count == 0) OnStageCrear(); } |
自機の弾丸と敵の当たり判定がtrueのときは敵を撃墜できたので点数を加算、敵と自機、敵弾丸の当たり判定がtrueのときはミスをしたことになります。それぞれ OnHitEnemy と OnMiss で処理をしています。もし敵がゼロになっていればステージクリアです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
void OnHitEnemy(PictureBox bullet, Enemy enemy) { bullet.Dispose(); enemy.Dispose(); } void OnMiss() { } int stage = 1; void OnStageCrear() { stage++; ShowStage(); CreateEnemies(); } void ShowStage() { labelStage.Text = "Stage " + stage.ToString(); } |
命中した場合は弾丸と敵をDisposeしています。これだけでは面白くないのでいろいろ追加していきます。
敵を撃墜したとき
敵を撃墜したときは点数を加算します。敵のIDを調べて得点を変えます。最上段は30点、2段目は20点、それ以外は10点、ただし攻撃中の敵を撃墜した場合は得点を2倍にします。
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 |
void OnHitEnemy(PictureBox bullet, Enemy enemy) { Point pt = enemy.Location; bullet.Dispose(); enemy.Dispose(); // 点数計算 int point = 0; if (enemy.ID < 100) point = 30; else if(enemy.ID < 200) point = 20; else point = 10; if (enemy.EnemyStatus == EnemyStatus.Attack1) point *= 2; score += point; ShowPoint(); // 音を鳴らす PlaySound2(); // 爆発させる。 ShowBom(pt); } |
音を鳴らして爆発イメージを0.5秒間表示させます。事前に画像をつくっておき、ゲーム開始時に非表示にして、タイマーをつかって一瞬だけ表示させています。また音はここから取得しました。
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 |
private void PlaySound2() { try { string path = Application.StartupPath + "\\2.wav"; var player = new System.Media.SoundPlayer(path); player.Play(); } catch { } } void ShowBom(Point pt) { pictureBoxHit.Parent = panel1; pictureBoxHit.Visible = true; pictureBoxHit.Location = pt; Timer timer = new Timer(); timer.Tick += Timer_Tick; timer.Start(); void Timer_Tick(object sender, EventArgs e) { Timer t = (Timer)sender; t.Stop(); t.Dispose(); pictureBoxHit.Visible = false; } } void ShowPoint() { string str = string.Format("{0:#,0} ", score); labelScore.Text = "Score "+ str.PadLeft(5); } |
ミスとゲームオーバー
通常のゲームでは自機は3機で全滅したらゲームオーバーです。そこでフィールド変数に残機を格納してミスするたびに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 |
int REST = 3; int rest = REST; void OnMiss() { rest--; ShowRest(); // 音を鳴らして背景を赤くする PlaySound3(); panel1.BackColor = Color.Red; pictureBoxSpaceship.Visible = false; Timer timer = new Timer(); timer.Tick += Timer_Tick; timer.Interval = 3000; timer.Start(); // 立て続けにやられないようにいったん敵弾をクリアして、敵ももとの位置に戻す timer1.Stop(); foreach (var bullet in EnemyManager.EnemyBullets) { bullet.Dispose(); } foreach (var enemy in EnemyManager.Enemies) { enemy.Location = enemy.OldPos; } foreach (var bullet in Bullets) { bullet.Dispose(); } // 元に戻す void Timer_Tick(object sender, EventArgs e) { Timer t = (Timer)sender; t.Stop(); t.Dispose(); panel1.BackColor = Color.Black; pictureBoxSpaceship.Visible = true; timer1.Start(); } if (rest == 0) { OnGameOver(); } } void ShowRest() { labelRest.Text = "残 "+ rest.ToString(); } |
残機がゼロになったらゲームオーバーです。
1 2 3 4 5 6 |
bool IsGameOver = false; // フィールド変数 void OnGameOver() { IsGameOver = true; } |
そして IsGameOver がtrueになったら、Timer1_Tickメソッドが呼ばれても何もしません。タイマーも止めてしまいます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
private void Timer1_Tick(object sender, EventArgs e) { if (IsGameOver) { timer1.Stop(); MessageBox.Show("Game Over"); return; } countTimerTick++; SpaceshipMove(); BulletsMove(); StandingByEnemysMove(); BeginAttack(); AttackEnemiesMove(); EnemyManager.EnemyBulletsMove(); Judge(); } |
再びゲームをしたい場合は「S」キーを押せばできるようになっていますが、そのためには
止まっているタイマーを動かす
IsGameOverフラグをクリアする
現在の敵を消去して作り直す
得点をゼロにする
残機の表示
などが必要です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
int REST = 3; void GameStart() { foreach (Enemy enemy in EnemyManager.Enemies) enemy.Dispose(); rest = REST; ShowRest(); score = 0; ShowPoint(); stage = 1; ShowStage(); CreateEnemies(); IsGameOver = false; timer1.Start(); } |
終わりに
以上で、とりあえず完成とすることにします。他にはステージが進むにつれて敵の移動速度を速くする、編隊攻撃と単体攻撃では得点を変えるなど改良の余地がたくさんあります。各自で工夫してオリジナリティあふれるゲームをつくってください。