こんな感じのゲームをつくります。
タンクバタリアンは自機をすべて失ったときだけでなく、司令部を破壊された場合もゲームオーバーです。しかも即ゲームオーバー。ステージが進むにつれて敵の連射がエグくなってきます。
司令部があるのは「H」の位置です。
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 71 72 73 74 75 76 77 78 |
public partial class Form1 : Form { int HeadquartersX = 0; int HeadquartersZ = 0; void CreateStage(int i) { List<string> vs = LoadStageFile(i); CreateStage(vs); } void CreateStage(List<string> vs) { BorderBlocks.Clear(); Blocks.Clear(); int z = 0; enemyStart1X = -1; foreach(string str in vs) { char[] vs1 = str.ToArray(); int x = 0; foreach(char c in vs1) { if(c == '■') BorderBlocks.Add(new BorderBlock(x * 2, z * (-2))); if(c == '□') Blocks.Add(new Block(x * 2, z * (-2))); if(c == 'M') { myStartX = x * 2; myStartZ = z * -2; } if(c == 'E') { if(enemyStart1X == -1) { enemyStart1X = x * 2; enemyStart1Z = z * -2; } else { enemyStart2X = x * 2; enemyStart2Z = z * -2; } } if(c == 'H') { HeadquartersX = x * 2; HeadquartersZ = z * -2; } x++; } z++; } } List<string> LoadStageFile(int i) { string filePath = String.Format("{0}\\stage{1}.txt", Application.StartupPath, i); StreamReader sr = new StreamReader(filePath); List<string> vs = new List<string>(); while(true) { string str = sr.ReadLine(); if(str == null) break; vs.Add(str); } sr.Close(); vs.Reverse(); return vs; } } |
司令部がある座標を砲弾が通過したらゲームオーバーです。自分が撃った砲弾が当たった場合もゲームオーバーです。
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 |
public partial class Form1 : Form { new void Update() { foreach(Bullet bullet in Tank.Bullets) bullet.Move(); MyBulletsUpdate(); // 必要なら新しい戦車を出現させる。 AppearNewEnemyTank(); EnemyTanksUpdate(); EnemyBulletsUpdate(); if(IsMyTankDead()) { // 爆発音を鳴らす BombSound(); OnDead(); } if(IsHeadquartersDead()) OnHeadquartersDead(); ShowRadar(); } bool IsHeadquartersDead() { List<Bullet> bullets = new List<Bullet>(); bullets.AddRange(Tank.Bullets); foreach(EnemyTank enemyTank in EnemyTanks) bullets.AddRange(enemyTank.Bullets); Bullet bullet1 = bullets.FirstOrDefault(x => Math.Round(x.X - HeadquartersX) == 0 && Math.Round(x.Z - HeadquartersZ) == 0); if(bullet1 != null) return true; return false; } void OnHeadquartersDead() { OnGameOver(); } void OnGameOver() { timer.Stop(); Text = "GameOver"; MessageBox.Show("GameOver"); } // 必要なら新しい戦車を出現させる。 void AppearNewEnemyTank() { appearEnemyCounter++; if(EnemyTanks.Count < MaxEnemyTank && appearEnemyCounter % appearEnemyInterval == 0) { // 左上に出現させるか? 右上に出現させるか? if(Random.Next(2) == 0) EnemyTanks.Add(new EnemyTank(this, enemyStart1X, enemyStart1Z, Color.Blue, 90)); else EnemyTanks.Add(new EnemyTank(this, enemyStart2X, enemyStart2Z, Color.Blue, 90)); appearEnemyCounter = 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 68 69 70 |
public partial class Form1 : Form { int RestMax = 5; int Rest { get { if(_rest > 0) return _rest; else return 0; } set { if(value > 0) _rest = value; else _rest = 0; labelRest.Text = "残 " + _rest.ToString(); } } void Start() { // 最初は残機を最大値にする Rest = RestMax; EnemyTanks.Clear(); Explosions.Clear(); CreateStage(1); ResetMyTank(); ResetSight(); timer.Start(); } void OnDead() { Rest--; Timer timer1 = new Timer(); timer1.Interval = 3000; timer1.Tick += Timer1_Tick1; timer1.Start(); void Timer1_Tick1(object sender, EventArgs e) { Timer t = (Timer)sender; t.Stop(); t.Dispose(); if(Rest == 0) { OnGameOver(); return; } // 自分の戦車をもとの位置に戻す ResetMyTank(); ResetSight(); // 敵は一旦クリア EnemyTanks.Clear(); Tank.isDead = false; } } } |
また砲撃をするときは発射音、命中時は爆発音を出すことにします。
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 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
public partial class Form1 : Form { protected override bool ProcessDialogKey(Keys keyData) { if((keyData & Keys.KeyCode) == Keys.Space) { Tank.Shot(); ShotSound(); glControl.Refresh(); return true; } // 他のキーが押されたときの処理は省略 // return base.ProcessDialogKey(keyData); } private void ShotSound() { //再生するファイル名 string fileName = Application.StartupPath + "\\shot.mp3"; PlayMp3(fileName); } void MyBulletsUpdate() { // 砲弾は壁に当たったか? foreach(Bullet bullet in Tank.Bullets) { if(Blocks.Any(x => x.IsBombed(bullet))) { bullet.isDead = true; Explosions.Add(new Explosion(bullet.X, bullet.Z)); // 砲弾が壁に当たったら爆発音を鳴らす。 BombSound(); } } foreach(Bullet bullet in Tank.Bullets) { if(BorderBlocks.Any(x => x.IsBombed(bullet))) { bullet.isDead = true; Explosions.Add(new Explosion(bullet.X, bullet.Z)); // 砲弾が壁に当たったら爆発音を鳴らす。 BombSound(); } } // 砲弾は敵戦車に当たったか? foreach(Bullet bullet in Tank.Bullets) { EnemyTank hitedEnemyTank = EnemyTanks.FirstOrDefault(x => x.IsBombed(bullet)); if(hitedEnemyTank != null) { OnHitEnemyTank(hitedEnemyTank, bullet); } } // 砲弾は敵の砲弾に当たったか? foreach(Bullet bullet in Tank.Bullets) { foreach(EnemyTank enemyTank in EnemyTanks) { Bullet hit = enemyTank.Bullets.FirstOrDefault(x => Math.Abs(x.X - bullet.X) < 0.5 && Math.Abs(x.Z - bullet.Z) < 0.5); if(hit != null) { hit.isDead = true; bullet.isDead = true; break; } } } } private void BombSound() { string fileName = Application.StartupPath + "\\bomb.mp3"; PlayMp3(fileName); } private void PlayMp3(string fileName) { if(isPlayings.Count > 0 && isPlayings.Last()) { isPlayings[isPlayings.Count -1] = false; UrgentStopMp3(); } isPlayings.Add(true); string cmd; cmd = "open \"" + fileName + "\" type mpegvideo alias " + aliasName; if(mciSendString(cmd, null, 0, IntPtr.Zero) != 0) return; cmd = "play " + aliasName; mciSendString(cmd, null, 0, IntPtr.Zero); Timer timer = new Timer(); timer.Interval = 2000; timer.Tick += Timer_Tick1; timer.Start(); void Timer_Tick1(object sender1, EventArgs e1) { Timer timer1 = (Timer)sender1; timer1.Stop(); timer1.Dispose(); StopMp3(); } } private void StopMp3() { if(isPlayings[0]) { string cmd; cmd = "stop " + aliasName; mciSendString(cmd, null, 0, IntPtr.Zero); cmd = "close " + aliasName; mciSendString(cmd, null, 0, IntPtr.Zero); } isPlayings.RemoveAt(0); } private void UrgentStopMp3() { string cmd; cmd = "stop " + aliasName; mciSendString(cmd, null, 0, IntPtr.Zero); cmd = "close " + aliasName; mciSendString(cmd, null, 0, IntPtr.Zero); } } |
砲弾が敵の戦車に命中したらスコアを増やします。正面からまたは近距離から撃ったほうが獲得点数を高くします。
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 71 72 73 |
public partial class Form1 : Form { void OnHitEnemyTank(EnemyTank hitedEnemyTank, Bullet bullet) { bullet.isDead = true; hitedEnemyTank.isDead = true; Explosions.Add(new Explosion(hitedEnemyTank.X, hitedEnemyTank.Z)); BombSound(); AddScore(hitedEnemyTank); } void AddScore(EnemyTank hit) { int x = (int)Math.Round(Tank.X - hit.X); int z = (int)Math.Round(Tank.Z - hit.Z); bool isfront = false; if(x > 0 && hit.Direct == Direct.East) isfront = true; if(x < 0 && hit.Direct == Direct.West) isfront = true; if(z > 0 && hit.Direct == Direct.South) isfront = true; if(z < 0 && hit.Direct == Direct.North) isfront = true; int score = 0; double distance = Math.Sqrt(Math.Pow((Tank.X - hit.X), 2) + Math.Pow((Tank.Z - hit.Z), 2)); if(distance < 4) { if(isfront) score = 800; else score = 300; } else if(distance < 8) { if(isfront) score = 300; else score = 150; } else if(distance < 12) { if(isfront) score = 150; else score = 60; } else { if(isfront) score = 60; else score = 30; } Score += score; } int _score = 0; int Score { get { return _score; } set { _score = value; labelScore.Text = String.Format("{0:00000}", _score); } } } |
それから敵の戦車を一定数破壊するとステージクリアになります。1ステージの敵の戦車は20両です。
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 |
public partial class Form1 : Form { int EnemyCountMax = 20; int _enemyCount = 0; int EnemyCount { get { return _enemyCount; } set { _enemyCount = value; labelEnemyCount.Text = "残 " + _enemyCount; } } void CreateStage(int i) { List<string> vs = LoadStageFile(i); CreateStage(vs); // 1ステージの敵の戦車を設定する EnemyCount = EnemyCountMax; } void OnHitEnemyTank(EnemyTank hitedEnemyTank, Bullet bullet) { bullet.isDead = true; hitedEnemyTank.isDead = true; Explosions.Add(new Explosion(hitedEnemyTank.X, hitedEnemyTank.Z)); BombSound(); AddScore(hitedEnemyTank); EnemyCount--; if(EnemyCount == 0) { OnStageClear(); } } // 必要なら新しい戦車を出現させる // 残り敵戦車数よりも多く新しい戦車を出現させない void AppearNewEnemyTank() { appearEnemyCounter++; if(EnemyTanks.Count < MaxEnemyTank && appearEnemyCounter % appearEnemyInterval == 0 && EnemyCount > EnemyTanks.Count) { if(Random.Next(2) == 0) EnemyTanks.Add(new EnemyTank(this, enemyStart1X, enemyStart1Z, Color.Blue, 90)); else EnemyTanks.Add(new EnemyTank(this, enemyStart2X, enemyStart2Z, Color.Blue, 90)); appearEnemyCounter = 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 { // 現在のステージを表示するプロパティ int _stage = 0; int Stage { get { return _stage; } set { _stage = value; labelStage.Text = "STAGE " +_stage.ToString(); } } // ステージクリアしたら新しいステージへ void OnStageClear() { Timer timer = new Timer(); timer.Interval = 3000; timer.Tick += Timer_Tick1; timer.Start(); void Timer_Tick1(object sender, EventArgs e) { Timer t = (Timer)sender; t.Stop(); t.Dispose(); Stage++; // 新しいステージをつくるためのファイルを用意する CreateStage(2); ResetMyTank(); ResetSight(); } } } |