前回、作成した戦車から砲弾を発射させます。
スペースキーをおせば砲弾が発射されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public partial class Form1 : Form { protected override bool ProcessDialogKey(Keys keyData) { //キーの本来の処理を //させたくないときは、trueを返す if((keyData & Keys.KeyCode) == Keys.Space) { Tank.Shot(); glControl.Refresh(); return true; } // それ以外のキーの関しては前回と同じなので省略 return base.ProcessDialogKey(keyData); } } |
次にTankクラスのShot()メソッドですが、Bulletオブジェクトをつくってリストのなかに格納しているだけです。
1 2 3 4 5 6 7 8 9 |
public class Tank { public List<Bullet> Bullets = new List<Bullet>(); public void Shot() { Bullets.Add(new Bullet(this)); } } |
ではBulletクラスはどのようになっているのでしょうか?
砲弾は戦車の砲身の先からでてきます。砲身の先の座標は戦車が原点から動いていないのであれば(0,0.6,0.5)です。そこで戦車の位置を回転角度がわかれば砲身先の座標もわかります。砲弾の初期座標とX軸方向とZ軸方向の移動速度をコンストラクタ内で求めています。
それからBulletクラスのコンストラクタにはTank型の引数を渡します。砲弾がどの戦車から発射されたかわかるようにするためです。
初速がわかればMove()メソッドで発射後の砲弾の位置も計算できるようになります。また砲弾はどこまでも飛びません。重力の影響をうけます。やや水平ではなくやや上方に向けて発射しています。s = v0t – (1/2)gt^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 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 |
public class Bullet { public Bullet(Tank tank) { X = - Sin(-tank.RotateY)*(0.6f) + tank.X; Y = 0.6f; Z = Cos(-tank.RotateY) * (0.6f) + tank.Z; SpeedX = Speed * Sin(tank.RotateY); SpeedZ = Speed * Cos(tank.RotateY); } public float Speed { get { return 0.3f; } } public float SpeedX { get; private set; } public float SpeedZ { get; private set; } public float X { get; private set; } public float Y { get; private set; } = 0.6f; public float Z { get; private set; } public bool isDead { get; private set; } = false; public void Move() { // 着地した砲弾はこれ以上移動させない if(isDead) return; X += SpeedX; Z += SpeedZ; Time++; float time = Time / 100f; Y = 0.6f - 4.9f * time * time + 2f * time; if(Y <= 0) isDead = true; } public void Draw() { // X,Y,Zを中心とする立方体 大きさ0.1 GL.PushMatrix(); { GL.Translate(X, Y, Z); GL.Begin(BeginMode.Quads); { GL.Vertex3(0.05, 0.05, 0.05); GL.Vertex3(0.05, -0.05, 0.05); GL.Vertex3(-0.05, -0.05, 0.05); GL.Vertex3(-0.05, 0.05, 0.05); GL.Vertex3(-0.05, 0.05, 0.05); GL.Vertex3(-0.05, -0.05, 0.05); GL.Vertex3(-0.05, -0.05, -0.05); GL.Vertex3(-0.05, 0.05, -0.05); GL.Vertex3(0.05, 0.05, 0.05); GL.Vertex3(0.05, -0.05, 0.05); GL.Vertex3(0.05, -0.05, -0.05); GL.Vertex3(0.05, 0.05, -0.05); GL.Vertex3(0.05, 0.05, 0.05); GL.Vertex3(-0.05, 0.05, 0.05); GL.Vertex3(-0.05, 0.05, -0.05); GL.Vertex3(0.05, 0.05, -0.05); GL.Vertex3(0.05, -0.05, 0.05); GL.Vertex3(-0.05, -0.05, 0.05); GL.Vertex3(-0.05, -0.05, -0.05); GL.Vertex3(0.05, -0.05, -0.05); } GL.End(); } GL.PopMatrix(); } /// <summary> /// Sinを返す /// </summary> /// <param name="x">引数は弧度法ではなく度数法</param> /// <returns></returns> float Sin(double x) { return (float)Math.Sin(2 * Math.PI / 360 * x); } /// <summary> /// Sinを返す /// </summary> /// <param name="x">引数は弧度法ではなく度数法</param> /// <returns></returns> float Cos(double x) { return (float)Math.Cos(2 * Math.PI / 360 * x); } } |
物体を動かすのでタイマーを使います。
Tank.Bulletsのなかにオブジェクトがある場合は描画の処理をおこないます。
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 |
public partial class Form1 : Form { // タイマーを生成 Timer timer = new Timer(); List < Floor> Floors = new List<Floor>(); public Form1() { InitializeComponent(); ShowLabel(); Tank.Color = Color.Red; InitFloors(); InitTimer(); } // タイマーの初期化 void InitTimer() { timer.Interval = 50; timer.Tick += Timer_Tick; timer.Start(); } private void Timer_Tick(object sender, EventArgs e) { foreach(Bullet bullet in Tank.Bullets) bullet.Move(); glControl.Refresh(); } private void glControl_Paint(object sender, PaintEventArgs e) { GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); // 視点を変更する SetSight(); // このなかで砲弾の描画もおこなわれる Tank.Draw(); foreach(Floor floor in Floors) floor.Draw(); glControl.SwapBuffers(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public class Tank { public void Draw() { 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(); // 砲弾の描画 foreach(Bullet bullet in Bullets) bullet.Draw(); } } |