これまでは砲弾が命中しても戦車が爆破することはありませんでした。そこで今回は砲弾の当たり判定と爆破の描画をおこないます。
まず戦車と砲弾の当たり判定ができるように機能を追加します。
isDeadプロパティは戦車が生きているかどうかを示すプロパティです。
IsBombed(Bullet bullet)メソッドは砲弾が命中したかどうかを判定するメソッドです。敵の砲弾が命中した場合、isDeadプロパティはtrueとなります。
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 |
public class Tank { public bool isDead { get; set; } = false; public bool IsBombed(Bullet bullet) { // すでに死んでいるのであれば当たり判定は無意味 if(isDead) return false; if( (X - 0.35 < bullet.X && bullet.X < X + 0.35) && (Z - 0.5 < bullet.Z && bullet.Z < Z + 0.5) && (Y < bullet.Y && bullet.Y < Y + 0.7) ) { isDead = true; return true; } return false; } public void Draw() { // 死んでいるなら描画しない if(isDead) return; GL.PushMatrix(); { GL.Translate(X, Y, Z); GL.Rotate(RotateX, 1, 0, 0); GL.Rotate(RotateY, 0, 1, 0); GL.Rotate(RotateZ, 0, 0, 1); Draw0(); } GL.PopMatrix(); // 地面におちた砲弾は表示させない Bullets = Bullets.Where(x => !x.isDead).ToList(); foreach(Bullet bullet in Bullets) bullet.Draw(); } } |
命中したときの爆破を描画するためのクラスを作成します。
合計25枚の板を上空に放射状に飛ばします。一定の高さになったら下に落とします。
— ランラン (@THE_RANRAN) September 16, 2020
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 |
public class Explosion { public Explosion(float x, float z) { X = x; Z = z; } public float X { get; protected set; } public float Z { get; protected set; } // 爆発の高さ double Height = 0; // 爆発開始と比較した爆発の広がり double Extend = 1; // 爆発で吹き飛ばれたものが落ちているか bool fall = false; // 爆発はおさまったか? public bool isDead { get; protected set; } = false; int DrawCount = 0; public void Draw() { if(isDead) return; if(fall) Height -= 0.2; else Height += 0.2; Extend += 0.1; if(Height > 1.2) fall = true; DrawCount++; GL.PushMatrix(); { GL.Translate(X, 0, Z); Draw0(); } GL.PopMatrix(); // 爆発による破片がすべて着地したのであれば // 描画のための処理は必要ないので、isDead = trueにする if(Height < 0) isDead = true; } // 合計25枚の板を上空に放射状に飛ばす。一定の高さになったら下に落とす。 // 爆破の位置はX、Zプロパティで、 // 破片が飛ぶ高さと爆発の広がりはHeight、Extendプロパティで調整する public void Draw0() { GL.Material(MaterialFace.Front, MaterialParameter.Ambient, Color.Orange); for(int z = -2; z <= 2; z++) { GL.PushMatrix(); { GL.Translate(0, 0, 0.2 * z * Extend); for(int x = -2; x <= 2; x++) { GL.PushMatrix(); { GL.Translate(0.2 * x * Extend, Height, 0); if((x + z) % 2 ==0) GL.Rotate(40 * DrawCount, 1, 0, 0); if((x + z) % 2 == 1) GL.Rotate(20 * DrawCount, 0, 0, 1); GL.Begin(BeginMode.Quads); { GL.Vertex3(0.15, 0, 0.15); GL.Vertex3(0.15, 0, -0.15); GL.Vertex3(-0.15, 0, -0.15); GL.Vertex3(-0.15, 0, 0.15); } GL.End(); } GL.PopMatrix(); } } GL.PopMatrix(); } } } |
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 |
public partial class Form1 : Form { List<Explosion> Explosions = new List<Explosion>(); new void Update() { // 自分の戦車の砲弾を移動させる foreach(Bullet bullet in Tank.Bullets) bullet.Move(); // 敵の戦車の位置を移動させる if(!EnemyTank.isDead) EnemyTank.Move(); // 敵の戦車の砲弾を移動させる foreach(Bullet bullet in EnemyTank.Bullets) bullet.Move(); // 敵の戦車に砲弾は命中した場合は爆破する if(EnemyTank.Bullets.Any(x => !x.isDead && Tank.IsBombed(x))) Explosions.Add(new Explosion(Tank.X, Tank.Z)); // 敵の戦車の弾は命中した場合は爆破する if(Tank.Bullets.Any(x => !x.isDead && EnemyTank.IsBombed(x))) Explosions.Add(new Explosion(EnemyTank.X, EnemyTank.Z)); } private void glControl_Paint(object sender, PaintEventArgs e) { GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); SetSight(); // 戦車が死んでいないのであれば描画する if(!Tank.isDead) Tank.Draw(); if(!EnemyTank.isDead) EnemyTank.Draw(); foreach(Floor floor in Floors) floor.Draw(); // 爆破の描画が必要であれば描画する Explosions = Explosions.Where(x => !x.isDead).ToList(); foreach(Explosion explosion in Explosions) { explosion.Draw(); } glControl.SwapBuffers(); } } |
それから砲弾が戦車に命中しないで着弾したときも、小さな爆発はするはずです。そこで小さな爆発の描画をおこないます。
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 |
public class SmallExplosion { public SmallExplosion(float x, float z) { X = x; Z = z; } public float X { get; protected set; } public float Z { get; protected set; } // 爆発の高さ double Height = 0; // 爆発開始と比較した爆発の広がり double Extend = 1; // 爆発で吹き飛ばれたものが落ちているか bool fall = false; // 爆発はおさまったか? public bool isDead { get; protected set; } = false; int DrawCount = 0; public void Draw() { if(isDead) return; if(fall) Height -= 0.1; else Height += 0.1; Extend += 0.1; if(Height > 0.7) fall = true; DrawCount++; GL.PushMatrix(); { GL.Translate(X, 0, Z); Draw0(); } GL.PopMatrix(); if(Height < 0) isDead = true; } public void Draw0() { GL.Material(MaterialFace.Front, MaterialParameter.Ambient, Color.Orange); for(int z = -2; z <= 2; z++) { GL.PushMatrix(); { GL.Translate(0, 0, 0.1 * z * Extend); for(int x = -1; x <= 1; x++) { GL.PushMatrix(); { GL.Translate(0.1 * x * Extend, Height, 0); if((x + z) % 3 == 0) GL.Rotate(40 * DrawCount, 1, 0, 0); if((x + z) % 3 == 1) GL.Rotate(20 * DrawCount, 0, 0, 1); GL.Begin(BeginMode.Quads); { GL.Vertex3(0.075, 0, 0.075); GL.Vertex3(0.075, 0, -0.075); GL.Vertex3(-0.075, 0, -0.075); GL.Vertex3(-0.075, 0, 0.075); } GL.End(); } GL.PopMatrix(); } } GL.PopMatrix(); } } } |
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 |
public partial class Form1 : Form { new void Update() { foreach(Bullet bullet in Tank.Bullets) bullet.Move(); if(!EnemyTank.isDead) EnemyTank.Move(); foreach(Bullet bullet in EnemyTank.Bullets) bullet.Move(); if(EnemyTank.Bullets.Any(x => !x.isDead && Tank.IsBombed(x))) Explosions.Add(new Explosion(Tank.X, Tank.Z)); if(Tank.Bullets.Any(x => !x.isDead && EnemyTank.IsBombed(x))) Explosions.Add(new Explosion(EnemyTank.X, EnemyTank.Z)); // 地面におちた砲弾を集める // この段階ではYプロパティが0以下になっている砲弾はリストから排除されていない // Yプロパティが0以下になっている砲弾がリストから排除されるのは // glControl_Paint(object sender, PaintEventArgs e)内で // Tank.Draw()メソッドが呼び出されるとき List<Bullet> Bullets1 = Tank.Bullets.Where(x => x.Y <= 0).ToList(); List<Bullet> Bullets2 = EnemyTank.Bullets.Where(x => x.Y <= 0).ToList(); List<SmallExplosion> smallExplosions1 = Bullets1.Select(x => new SmallExplosion(x.X, x.Z)).ToList(); List<SmallExplosion> smallExplosions2 = Bullets2.Select(x => new SmallExplosion(x.X, x.Z)).ToList(); SmallExplosions.AddRange(smallExplosions1); SmallExplosions.AddRange(smallExplosions2); } } private void glControl_Paint(object sender, PaintEventArgs e) { GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); SetSight(); if(!Tank.isDead) Tank.Draw(); if(!EnemyTank.isDead) EnemyTank.Draw(); foreach(Floor floor in Floors) floor.Draw(); // 爆破の描画が不要になったExplosionオブジェクトは取り除く Explosions = Explosions.Where(x => !x.isDead).ToList(); foreach(Explosion explosion in Explosions) explosion.Draw(); // 爆破の描画が不要になったSmallExplosionオブジェクトは取り除く SmallExplosions = SmallExplosions.Where(x => !x.isDead).ToList(); foreach(SmallExplosion explosion in SmallExplosions) explosion.Draw(); glControl.SwapBuffers(); } } |