の続きです。今回は敵の戦車をつくります。
敵の戦車の移動方法は2秒間前進し、そのあと自機がある方向に方向転換します。つねに正しい方向に動くことができると人間側としては対抗のしようがないので制約をつけます。
2秒間前進
方向転換をする必要性がないときも方向転換のために戦車を停車させる
それではやってみましょう。
Tankクラスを継承してEnemyTankクラスを作成します。戦車を動かすためのメソッドとしてGo()、Back()、TurnLeft()、TurnRight()メソッドを追加しています。
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 |
public class EnemyTank : Tank { public EnemyTank(Form1 form1, float x, float z, Color color, int angle) : base(x, z, color, angle) { MainForm = form1; } Form1 MainForm = null; /// <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); } /// <summary> /// 弧度法を度数法に変換する /// </summary> /// <param name="d"></param> /// <returns></returns> int GetAngleFromRad(double d) { return (int)(d / (2 * Math.PI) * 360); } public float Angle { get { return RotateY; } set { RotateY = value; if(RotateY > 360) RotateY -= 360; if(RotateY < 0) RotateY += 360; } } public void Go() { X += Sin((int)Angle) * 0.1f; Z += Cos((int)Angle) * 0.1f; } public void Back() { X -= Sin((int)Angle) * 0.1f; Z -= Cos((int)Angle) * 0.1f; } public void TurnLeft() { Angle += 5; } public void TurnRight() { Angle -= 5; } } |
EnemyTankクラスのMove()で敵の戦車を移動させるのですが、2秒おきにisGoフラグを切り替えています。問題はisGo == false のときです。自分の戦車と敵の戦車の位置関係から戦車が向くべき方向を求めています。
それぞれのX座標とZ座標をもとにarctan(アークタンジェント)関数をつかって角度を求めれば簡単だと思っていたらそうではありませんでした。戦車の位置関係によって180度逆の結果が出てくることがあるので、戦車の位置関係で処理を変える必要があったのです。
戦車の方向転換が終了したら弾丸を発射します。方向転換しているときは逃げるか止まっているのを利用して砲撃しないとやられます。ただいまのところは当たり判定の処理はしていません。お互い砲撃しても弾丸は貫通するだけです。
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 |
public class EnemyTank : Tank { bool isGo = true; int moveCount = 0; public void Move() { moveCount++; int interval = MainForm.GetTimerInterval(); if(moveCount * interval >= 2000) { if(isGo == true) isGo = false; else { isGo = true; Shot(); } moveCount = 0; } if(isGo) Go(); else { float x = MainForm.Tank.X - X; float z = -(MainForm.Tank.Z - Z); double rad = Math.Atan(z / x); int angle = GetAngleFromRad(rad); // 戦車が向くべき方向 double tankAngle; if(x / z > 0) { if(MainForm.Tank.Z > Z) tankAngle = angle + 270; else tankAngle = angle + 90; } else { if(MainForm.Tank.Z > Z) tankAngle = angle + 90; else tankAngle = angle + 270; } // 戦車が向くべき方向に向かせる if(Angle < tankAngle) { if(tankAngle - Angle < 180) TurnLeft(); else TurnRight(); } if(Angle > tankAngle) { if(Angle - tankAngle < 180) TurnRight(); else TurnLeft(); } } } } |
1 2 3 4 5 6 7 8 |
public partial class Form1 : Form { // タイマーのIntervalプロパティを取得する public int GetTimerInterval() { return timer.Interval; } } |
これで敵の戦車は2秒ごとにこちらの戦車の方向に向かって移動することができるようになりました。ただし180度以上の方向転換をすると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 |
public partial class Form1 : Form { EnemyTank EnemyTank = null; public Form1() { InitializeComponent(); ShowLabel(); Tank.Color = Color.Red; InitFloors(); InitTimer(); EnemyTank = new EnemyTank(this, 3, -8, Color.Blue, 90); } private void Timer_Tick(object sender, EventArgs e) { Update(); glControl.Refresh(); } new void Update() { foreach(Bullet bullet in Tank.Bullets) bullet.Move(); EnemyTank.Move(); foreach(Bullet bullet in EnemyTank.Bullets) bullet.Move(); } private void glControl_Paint(object sender, PaintEventArgs e) { GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); SetSight(); Tank.Draw(); EnemyTank.Draw(); foreach(Floor floor in Floors) floor.Draw(); glControl.SwapBuffers(); } } |