こんな感じのゲームをつくります。
これまで自分の戦車だけしか動かすことができませんでした。今回は敵の戦車を出現させます。
敵の戦車は左右上方の角に出現します。そして基本的にUターンはしません(袋小路に入った場合は例外とする)。ときどき砲弾も発射させます。
敵が出現するのはEの位置です。それから自分の戦車の最初の位置はMです。そこでCreateStage(int i)メソッドを以下のように書き直します。
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 { int myStartX = 0; int myStartZ = 0; int enemyStart1X = 0; int enemyStart1Z = 0; int enemyStart2X = 0; int enemyStart2Z = 0; void CreateStage(int i) { string filePath = String.Format("{0}\\stage{1}.txt", Application.StartupPath, i); StreamReader sr = new StreamReader(filePath); List<string> vs1 = new List<string>(); while(true) { string str = sr.ReadLine(); if(str == null) break; vs1.Add(str); } sr.Close(); vs1.Reverse(); BorderBlocks.Clear(); Blocks.Clear(); int z = 0; enemyStart1X = -1; foreach(string str in vs1) { char[] vs = str.ToArray(); int x = 0; foreach(char c in vs) { 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; } } x++; } z++; } } } |
Mの位置がわかればその位置に自分の戦車を表示させることができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public partial class Form1 : Form { // 自分の戦車の位置・方向・状態をもとに戻す void ResetMyTank() { X = myStartX; Z = myStartZ; RotateY = 180; Tank.isDead = false; Tank.Bullets.Clear(); } } |
敵の戦車をリストで管理します。最大で5両出現します。EnemyTanksプロパティを定義して、フィールド上に存在し、かつ生き残っているものを取得できるようにしておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public partial class Form1 : Form { int MaxEnemyTank = 5; List<EnemyTank> _enemyTanks = new List<EnemyTank>(); List<EnemyTank> EnemyTanks { get { _enemyTanks = _enemyTanks.Where(x => !x.isDead).ToList(); return _enemyTanks; } set { _enemyTanks = value; } } } |
爆破の描画に必要なオブジェクトもプロパティで管理します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public partial class Form1 : Form { List<Explosion> _explosions = new List<Explosion>(); List<Explosion> Explosions { get { _explosions = _explosions.Where(x => !x.isDead).ToList(); return _explosions; } set { _explosions = value; } } } |
敵の戦車 EnemyTankクラスを作成します。Randomオブジェクトは方向転換のときに必要です。
1 2 3 4 5 6 7 8 9 10 11 |
public class EnemyTank : Tank { public EnemyTank(Form1 form1, float x, float z, Color color, int angle) : base(x, z, color, angle, form1) { MainForm = form1; } static Random Random = new Random((int)DateTime.Now.Ticks); Form1 MainForm = null; } |
移動はタイマーイベント1回で0.1ずつ移動させます。2.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 71 72 73 74 75 76 77 78 |
public class EnemyTank : Tank { int counter = 0; public void Move() { // 0.1ずつ移動。2.0移動したら方向転換が可能。 if(counter % 20 == 0) { counter = 0; // 現在の方向 Direct oldDirect = Direct; // 移動できる方向を取得 List<Direct> directs = new List<Direct>(); if(CanMove(Direct.North)) directs.Add(Direct.North); if(CanMove(Direct.South)) directs.Add(Direct.South); if(CanMove(Direct.West)) directs.Add(Direct.West); if(CanMove(Direct.East)) directs.Add(Direct.East); // 原則、Uターンはしない if(directs.Count > 1) { if(oldDirect == Direct.North) directs.Remove(Direct.South); if(oldDirect == Direct.South) directs.Remove(Direct.North); if(oldDirect == Direct.East) directs.Remove(Direct.West); if(oldDirect == Direct.West) directs.Remove(Direct.East); } // 移動する方向を決定 int r = Random.Next(directs.Count); Direct direct = directs[r]; if(direct == Direct.North) RotateY = 180; else if(direct == Direct.South) RotateY = 0; else if(direct == Direct.West) RotateY = 270; else if(direct == Direct.East) RotateY = 90; } Move0(); Shot(); counter++; } void Move0() { if(RotateY == 0) { Z += 0.1f; } else if(RotateY == 90) { X += 0.1f; } else if(RotateY == 180) { Z += -0.1f; } else if(RotateY == 270) { X += -0.1f; } } } |
Shot()メソッドは砲弾を発射するためのメソッドです。タイマーイベント60回に1回のペースで砲撃させます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public class EnemyTank : Tank { // タイマーイベント60回に1回のペースで砲撃する int ShotInterval = 60; int ShotIntervalCounter = 0; new public void Shot() { if(ShotIntervalCounter % ShotInterval == 0) { Bullets.Add(new Bullet(this)); ShotIntervalCounter = 0; } ShotIntervalCounter++; } } |
次にForm1クラスでの処理ですが、敵の新しい戦車は出現するのか? 敵戦車と敵の発射した砲弾を移動させ、命中したかどうかの判定と命中時の処理をおこないます。
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 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 { // タイマーイベント50回で敵が5未満のとき新しい敵を出現させる。 int appearEnemyCounter = 0; int appearEnemyInterval = 50; Random Random = new Random(DateTime.Now.Millisecond); new void Update() { foreach(Bullet bullet in Tank.Bullets) bullet.Move(); MyBulletsUpdate(); // 戦車は出現するか? 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; } EnemyTanksUpdate(); EnemyBulletsUpdate(); if(IsMyTankDead()) { OnDead(); } ShowRadar(); } 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)); } } foreach(Bullet bullet in Tank.Bullets) { if(BorderBlocks.Any(x => x.IsBombed(bullet))) { bullet.isDead = true; Explosions.Add(new Explosion(bullet.X, bullet.Z)); } } // 砲弾は敵戦車に当たったか? foreach(Bullet bullet in Tank.Bullets) { EnemyTank hit = EnemyTanks.FirstOrDefault(x => x.IsBombed(bullet)); if(hit != null) { bullet.isDead = true; hit.isDead = true; Explosions.Add(new Explosion(hit.X, hit.Z)); } } // 砲弾は敵の砲弾に当たったか? 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; } } } } void EnemyTanksUpdate() { foreach(EnemyTank enemyTank in EnemyTanks) { if(!Tank.isDead) { enemyTank.Move(); } } } void EnemyBulletsUpdate() { foreach(EnemyTank enemyTank in EnemyTanks) { foreach(Bullet bullet in enemyTank.Bullets) { bullet.Move(); // 敵の砲弾は壁に当たったか? if(Blocks.Any(x => x.IsBombed(bullet))) { bullet.isDead = true; Explosions.Add(new Explosion(bullet.X, bullet.Z)); } if(BorderBlocks.Any(x => x.IsBombed(bullet))) { bullet.isDead = true; Explosions.Add(new Explosion(bullet.X, bullet.Z)); } } } } // 敵の砲弾は自分の戦車に当たったか? bool IsMyTankDead() { foreach(EnemyTank enemyTank in EnemyTanks) { Bullet bullet1 = enemyTank.Bullets.FirstOrDefault(x => !x.isDead && Tank.IsBombed(x)); if(bullet1 != null) { enemyTank.Bullets.Remove(bullet1); Explosions.Add(new Explosion(Tank.X, Tank.Z)); return true; } } return false; } } |
敵にやられたときは敵は一旦クリア、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 |
public partial class Form1 : Form { void OnDead() { // 生き返るためのタイマーをセットする Timer timer1 = new Timer(); timer1.Interval = 3000; timer1.Tick += Timer1_Tick1; timer1.Start(); } private void Timer1_Tick1(object sender, EventArgs e) { Timer t = (Timer)sender; t.Stop(); t.Dispose(); // 自分の戦車をもとの位置に戻す 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 |
public partial class Form1 : Form { private void PanelRadar_Paint(object sender, PaintEventArgs e) { BlocksShowRadar(e.Graphics); MyTankShowRadar(e.Graphics); BulletsShowRadar(e.Graphics); EnemyTankShowRadar(e.Graphics); EnemyTankBulletsShowRadar(e.Graphics); return; } void EnemyTankShowRadar(Graphics g) { int size = 16; int leftMargin = 1; int min = (int)BorderBlocks.Min(x => x.Back); min = Math.Abs(min); foreach(EnemyTank enemyTank in EnemyTanks) { Point tankPos = new Point((int)Math.Round(enemyTank.X), (int)Math.Round(enemyTank.Z)); int x = (tankPos.X + leftMargin) * size / 2 + 2; int y = (tankPos.Y + min) * size / 2 + 2; TankShowRadar(enemyTank, g, x, y); } } void EnemyTankBulletsShowRadar(Graphics g) { int size = 16; int leftMargin = 1; int min = (int)BorderBlocks.Min(x => x.Back); min = Math.Abs(min); foreach(EnemyTank enemyTank in EnemyTanks) { List<Point> pt = new List<Point>(); var bullets = enemyTank.Bullets.Where(x => !x.isDead).ToList(); foreach(Bullet bullet in bullets) pt.Add(new Point((int)bullet.X, (int)bullet.Z)); List<Rectangle> rectangles = pt.Select(x => { return new Rectangle((x.X + leftMargin) * size / 2 + 5, (x.Y + min) * size / 2 + 5, 6, 6); }).ToList(); if(rectangles.Count > 0) g.FillRectangles(new SolidBrush(Color.White), rectangles.ToArray()); } } } |