今回は自車だけでなく、ライバル車も走らせます。このときに問題になるのがライバル車の動きです。今回は簡単にするためにコースの真ん中を走らせます(下の動画を参照)。
ライバル車を制御する
ライバル車がいる座標にもっとも近いコースの外側の座標と内側の座標を求めます。これを結んでできる線分の中点がコースの真ん中です。そしてこの線分に直角に交わる直線の方向ベクトルがライバル車が移動すべき方向です。
まずライバル車をつくります。ライバル車を登場させるからにはコースにあわせて方向転換させなければなりません。方向転換処理以外の部分を示します。
コンストラクタは自車を描画するための基底クラスと同じです。
Updateメソッドですが、自車の場合は方向キーを押すことで変化したCar.RotateやCar.SpeedからX方向とZ方向の速度を算出して位置を移動させればよかったのですが、ライバル車の場合は手動ではなく自動でなんらかの処理をさせなければなりません。そこでTurnAroundメソッドを新たに追加します。
Drawメソッドは方向転換をしているときは回転角を大きめに設定していましたが、ライバル車は通常の回転処理にします。実際に動かしてみるとかなりジグザグ運転をしてくれます。
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 |
public class RivalCar : Car { public RivalCar(GameManager gameManager, CarData carData) : base(gameManager, carData) { } public override void Update() { TurnAround(); double rad = Math.PI / 180 * Rotate; VecX = Speed * (float)Math.Cos(rad); VecZ = -Speed * (float)Math.Sin(rad); base.Update(); } public override void Draw() { GL.PushMatrix(); { GL.Translate(X, Y, Z); GL.Rotate(Rotate + 90, 0, 1, 0); if (CarData != null) DrawCar(CarData); } GL.PopMatrix(); } } |
方向転換の処理
次にTurnAroundメソッドですが、まずライバル車の現在位置からもっとも近い左右のブロック(コースとの境界)の座標を求めます。そしてこれに直交する方向がライバル車の向きです。ただし常にこの処理をおこなうと酷いジグザグ運転になるので、距離にして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 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 |
public class RivalCar : Car { bool FirstMove = true; void TurnAround() { // 最適化された進行角度を求める PointF[] pointFs = GameManager.Course.GetNeerestExpandedBorderPoints(new PointF(this.X, this.Z)); double rad = Math.Atan2(pointFs[1].Y - pointFs[0].Y, pointFs[1].X - pointFs[0].X); double angle = 90 - 180 * rad / Math.PI; double differenceFromIdeal; PointF pointF = GameManager.Course.IdealPoint(new PointF(this.X, this.Z)); // 速度ゼロのとき、最初のUpdateでは最適化された進行角度を設定する if (this.Speed == 0 || FirstMove) { this.Rotate = (int)angle; this.X = pointF.X; this.Z = pointF.Y; FirstMove = false; } // 理想の位置から大きくズレた場合は方向転換をする double dx = this.X - pointF.X; double dz = this.Z - pointF.Y; differenceFromIdeal = Math.Sqrt(Math.Pow(dx, 2) + Math.Pow(dz, 2)); if (differenceFromIdeal > 3) { this.Rotate = (int)angle; // コースの端に寄っている場合はさらに方向転換をする // どちら側に寄っているのか? double dx1 = this.X - pointFs[0].X; double dz1 = this.Z - pointFs[0].Y; double distance1 = Math.Sqrt(Math.Pow(dx1, 2) + Math.Pow(dz1, 2)); double dx2 = this.X - pointFs[1].X; double dz2 = this.Z - pointFs[1].Y; double distance2 = Math.Sqrt(Math.Pow(dx2, 2) + Math.Pow(dz2, 2)); // 左寄りなので右にハンドルを切る if (distance1 < distance2) { if (this.Speed > 0) this.Rotate -= 1; } // 右寄りなので左にハンドルを切る else { if (this.Speed > 0) this.Rotate += 1; } } } } |
それからRivalCarクラスのなかでまとめて動かせるように以下も追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class RivalCar : Car { static List<RivalCar> RivalCars = new List<RivalCar>(); public static void Add(RivalCar rivalCar) { RivalCars.Add(rivalCar); } public static void UpdateAll() { RivalCars.ForEach(x => x.Update()); } public static void DrawAll() { RivalCars.ForEach(x => x.Draw()); } } |
実際に走らせてみる
あとはライバル車を追加して走らせるだけです。
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 class GameManager { public void OnGLControlLoad() { MyCar = new Car(this, ReadCarFiles(Application.StartupPath + "\\OrangeCarData1.xml")); MyCar.InitPosition(148 * (float)ExpansionRate, 120 * (float)ExpansionRate); InitRivalCars(); } void InitRivalCars() { CarData carData1 = ReadCarFiles(Application.StartupPath + "\\YellowCarData1.xml"); CarData carData2 = ReadCarFiles(Application.StartupPath + "\\BlueCarData1.xml"); CarData carData3 = ReadCarFiles(Application.StartupPath + "\\RedCarData1.xml"); CarData carData4 = ReadCarFiles(Application.StartupPath + "\\BlueCarData1.xml"); RivalCar rivalCar; rivalCar = new RivalCar(this, carData1); rivalCar.InitPosition(155 * (float)ExpansionRate, 121 * (float)ExpansionRate); rivalCar.Speed = 0.7f; RivalCar.Add(rivalCar); rivalCar = new RivalCar(this, carData2); rivalCar.InitPosition(190 * (float)ExpansionRate, 82 * (float)ExpansionRate); rivalCar.Speed = 0.9f; RivalCar.Add(rivalCar); rivalCar = new RivalCar(this, carData3); rivalCar.InitPosition(240 * (float)ExpansionRate, 120 * (float)ExpansionRate); rivalCar.Speed = 0.7f; RivalCar.Add(rivalCar); rivalCar = new RivalCar(this, carData4); rivalCar.InitPosition(280 * (float)ExpansionRate, 80 * (float)ExpansionRate); rivalCar.Speed = 0.75f; RivalCar.Add(rivalCar); rivalCar = new RivalCar(this, carData2); rivalCar.InitPosition(160 * (float)ExpansionRate, 210 * (float)ExpansionRate); rivalCar.Speed = 0.80f; RivalCar.Add(rivalCar); rivalCar = new RivalCar(this, carData3); rivalCar.InitPosition(185 * (float)ExpansionRate, 240 * (float)ExpansionRate); rivalCar.Speed = 0.80f; RivalCar.Add(rivalCar); rivalCar = new RivalCar(this, carData4); rivalCar.InitPosition(300 * (float)ExpansionRate, 240 * (float)ExpansionRate); rivalCar.Speed = 0.85f; RivalCar.Add(rivalCar); rivalCar = new RivalCar(this, carData2); rivalCar.InitPosition(460 * (float)ExpansionRate, 260 * (float)ExpansionRate); rivalCar.Speed = 0.87f; RivalCar.Add(rivalCar); rivalCar = new RivalCar(this, carData3); rivalCar.InitPosition(440 * (float)ExpansionRate, 245 * (float)ExpansionRate); rivalCar.Speed = 0.90f; RivalCar.Add(rivalCar); rivalCar = new RivalCar(this, carData4); rivalCar.InitPosition(205 * (float)ExpansionRate, 335 * (float)ExpansionRate); rivalCar.Speed = 0.95f; RivalCar.Add(rivalCar); } } |
更新と描画処理を示します。
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 |
public class GameManager { int UpdateCount = 0; void Update() { UpdateCount++; UpdateMyCar(); RivalCar.UpdateAll(); } // もともとUpdateメソッドのなかにあったものをまとめ直しただけ。 void UpdateMyCar() { if (LeftKeyDown) MyCar.Rotate += 2; else if (RightKeyDown) MyCar.Rotate -= 2; MyCar.Update(); if (CarStatus == CarStatus.Nomal) UpdateCarStatusNomal(); else if (CarStatus == CarStatus.Crash) UpdateCarStatusCrash(); Form1.label1.Text = String.Format("速度 {0} Km/h", Math.Round(MyCar.Speed, 2) * 100); } public void Draw() { GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); GL.ClearColor(Color.LightBlue); Update(); SetSight(); Lighting(); DrawCourse(); MyCar.Draw(); RivalCar.DrawAll(); GLControlEx.SwapBuffers(); } } |