前回 描画した戦車を移動させます。移動にともなう視界の移動もやってみます。
そのまえに戦車だけでなく地面も描画することにします。
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 |
public class Floor { public Floor(float x, float z, float width, float depth, Color color) { X = x; Z = z; Width = width; Depth = depth; Color = color; } public float X { get; private set; } public float Z { get; private set; } public float Width { get; private set; } public Color Color { get; set; } public float Depth { get; private set; } public float Height { get { return 0f; } } public void Draw() { GL.Material(MaterialFace.Front, MaterialParameter.Ambient, Color); GL.Normal3(Vector3.UnitY); GL.PushMatrix(); { GL.Translate(X, 0, Z); GL.Begin(BeginMode.Quads); { GL.Vertex3(Width / 2, Height, Depth / 2); GL.Vertex3(Width / 2, Height, -Depth / 2); GL.Vertex3(-Width / 2, Height, -Depth / 2); GL.Vertex3(-Width / 2, Height, Depth / 2); } GL.End(); } GL.PopMatrix(); } } |
あとはFloorオブジェクトを生成して描画処理をするだけです。
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 |
public partial class Form1 : Form { public Form1() { InitializeComponent(); ShowLabel(); Tank.Color = Color.Red; InitFloors(); } List<Floor> Floors = new List<Floor>(); void InitFloors() { for(int x = -10; x<10; x++) { for(int z = -10; z < 10; z++) { if((x + z) % 2 == 0) Floors.Add(new Floor(x * 2, z * 2, 2f, 2f, Color.White)); else Floors.Add(new Floor(x * 2, z * 2, 2f, 2f, Color.Gray)); } } } 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 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 |
public partial class Form1 : Form { float X { get { return (float)Tank.X; } set { Tank.X = value; ShowLabel(); SetSight(); } } float Y { get { return (float)Tank.Y; } set { Tank.Y = value; ShowLabel(); SetSight(); } } float Z { get { return (float)Tank.Z; } set { Tank.Z = value; ShowLabel(); SetSight(); } } void ShowLabel() { labelRotateX.Text = RotateX.ToString(); labelRotateY.Text = RotateY.ToString(); labelRotateZ.Text = RotateZ.ToString(); labelEyeX.Text = EyeX.ToString(); labelEyeY.Text = EyeY.ToString(); labelEyeZ.Text = EyeZ.ToString(); labelTargetX.Text = TargetX.ToString(); labelTargetY.Text = TargetY.ToString(); labelTargetZ.Text = TargetZ.ToString(); labelPosisionX.Text = "X = " + X.ToString(); labelPosisionY.Text = "Y = " + Y.ToString(); labelPosisionZ.Text = "Z = " + Z.ToString(); labelAngle.Text = RotateY.ToString() + "°"; } } |
Tankクラスにも以下を追加します。
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 |
public class Tank { // 戦車の位置 X座標 public float X { get; set; } = 0; // 戦車の位置 Y座標 public float Y { get; set; } = 0; // 戦車の位置 Z座標 public float Z { get; set; } = 0; 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(); } } 方向キーをおしたら前進、バック、方向転換ができるようにします。 これで戦車が移動できるようになりました。 <pre class="lang:default decode:true " > public partial class Form1 : Form { protected override bool ProcessDialogKey(Keys keyData) { //キーの本来の処理を //させたくないときは、trueを返す if((keyData & Keys.KeyCode) == Keys.Left) { if(checkBoxMove.Checked) { TurnLeft(); return true; } OnDown(); return true; } else if((keyData & Keys.KeyCode) == Keys.Right) { if(checkBoxMove.Checked) { TurnRight(); return true; } OnUp(); return true; } else if((keyData & Keys.KeyCode) == Keys.Up) { if(checkBoxMove.Checked) { Go(); return true; } OnUp(); return true; } else if((keyData & Keys.KeyCode) == Keys.Down) { if(checkBoxMove.Checked) { Back(); return true; } OnDown(); return true; } return base.ProcessDialogKey(keyData); } void TurnLeft() { RotateY += 5; glControl.Refresh(); } void TurnRight() { RotateY -= 5; glControl.Refresh(); } void Go() { X += Sin((int)RotateY) * 0.1f; Z += Cos((int)RotateY) * 0.1f; glControl.Refresh(); } void Back() { X -= Sin((int)RotateY) * 0.1f; Z -= Cos((int)RotateY) * 0.1f; glControl.Refresh(); } } |
また[戦車の動きにカメラを連動させる]がチェックされているときは、戦車の動きにあわせてEyeX、EyeZ、TargetX、TargetZの値を変更します。この場合、戦車が表示されている位置と方向はかわらず、背景がかわるように見えます(その効果を確認するためチェック模様の地面を描画した)。
EyeX、EyeZ、TargetX、TargetZの値はどのように変更すればよいのでしょうか? 戦車のある位置を中心にEyeとTargetを回転すればよいことになります。いったん原点に平行移動して回転移動させ、もとの位置に平行移動させてもどします。
回転処理は一次変換を使っています。通常の一次変換は、x = Cos(θ) * x – Sin(θ) * y、y = Sin(θ) * x + Cos(θ) * yなのですが、座標軸の方向が違うので符号を逆にしています。
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 |
public partial class Form1 : Form { /// <summary> /// Sinを返す /// </summary> /// <param name="x">引数は弧度法ではなく度数法</param> /// <returns></returns> float Sin(int x) { return (float)Math.Sin(2 * Math.PI / 360 * x); } /// <summary> /// Cosを返す /// </summary> /// <param name="x">引数は弧度法ではなく度数法</param> /// <returns></returns> float Cos(int x) { return (float)Math.Cos(2 * Math.PI / 360 * x); } void TurnLeft() { RotateY += 5; if(checkBoxTogether.Checked) { float x1 = EyeX; float z1 = EyeZ; EyeX = Cos(-5) * (x1 - X) - Sin(-5) * (z1 - Z) + X; EyeZ = Sin(-5) * (x1 - X) + Cos(-5) * (z1 - Z) + Z; float x2 = TargetX; float z2 = TargetZ; TargetX = Cos(-5) * (x2 - X) - Sin(-5) * (z2 - Z) + X; TargetZ = Sin(-5) * (x2 - X) + Cos(-5) * (z2 - Z) + Z; } glControl.Refresh(); } void TurnRight() { RotateY -= 5; if(checkBoxTogether.Checked) { float x1 = EyeX; float z1 = EyeZ; EyeX = Cos(5) * (x1 - X) - Sin(5) * (z1 - Z) + X; EyeZ = Sin(5) * (x1 - X) + Cos(5) * (z1 - Z) + Z; float x2 = TargetX; float z2 = TargetZ; TargetX = Cos(5) * (x2 - X) - Sin(5) * (z2 - Z) + X; TargetZ = Sin(5) * (x2 - X) + Cos(5) * (z2 - Z) + Z; } glControl.Refresh(); } void Go() { X += Sin((int)RotateY) * 0.1f; Z += Cos((int)RotateY) * 0.1f; if(checkBoxTogether.Checked) { EyeX += Sin((int)RotateY) * 0.1f; EyeZ += Cos((int)RotateY) * 0.1f; TargetX += Sin((int)RotateY) * 0.1f; TargetZ += Cos((int)RotateY) * 0.1f; } glControl.Refresh(); } void Back() { X -= Sin((int)RotateY) * 0.1f; Z -= Cos((int)RotateY) * 0.1f; if(checkBoxTogether.Checked) { EyeX -= Sin((int)RotateY) * 0.1f; EyeZ -= Cos((int)RotateY) * 0.1f; TargetX -= Sin((int)RotateY) * 0.1f; TargetZ -= Cos((int)RotateY) * 0.1f; } glControl.Refresh(); } } |