Contents
敵の動作を考えるEnemyThinkクラス
今回は敵の動作を考えます。そのために敵の行動を考えるEnemyThinkクラスを作成します。そしてEnemyMethodメソッド内でEnemyThink.Thinkメソッドを呼び出して敵がとる次の行動を考えます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public partial class Form1 : Form { EnemyThink EnemyThink; public Form1() { // Enemy, Player, Sunのインスタンス生成後に・・・ EnemyThink = new EnemyThink(Enemy, Player, Sun); } void EnemyMethod() { if (Enemy.IsDead || Player.IsDead) return; EnemyThink.Think(); } } |
EnemyThinkクラスのThinkメソッドは敵が太陽に近い位置にいる場合は回避行動をとらせ、そうでない場合は自機がいる方向に回頭させます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public class EnemyThink { Player Enemy; Player Jiki; Sun Sun; Random Random = new Random(); public EnemyThink(Player enemy, Player jiki, Sun sun) { Enemy = enemy; Jiki = jiki; Sun = sun; } public void Think() { // 太陽に対する回避行動をしないのであれば自機がいる方向に回頭 if (!AvoidCollisionWithSunIfNeed()) TurnTowardsJiki(); } } |
太陽に背を向ける角度とは?
まず太陽に吸い込まれては負けなので太陽の近くにいるときはそこから脱出するための処理を考えます。
太陽に対しては自分の背を向けるのが理想なのでその角度を求めます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class EnemyThink { int GetIdealAngleToSun(int x, int y) { double dx = x - Sun.Center.X; double dy = y - Sun.Center.Y; double rad = Math.Atan2(dy, dx); double idealAngle = 180 * rad / Math.PI + 90; // 戻り値が0~360になるようにする if (idealAngle < 0) idealAngle += 360; else if (idealAngle > 360) idealAngle -= 360; return (int)idealAngle; } } |
GetCorrectionAngleメソッドは現在の角度と理想の角度の差を返します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class EnemyThink { int GetCorrectionAngle(int angle, int idealAngle) { int difference = idealAngle - angle; // 戻り値が-180~ 180になるようにする if (difference > 180) difference = -(360 - difference); else if (difference < -180) difference = (360 + difference); return difference; } } |
太陽と充分離れているか?
AvoidCollisionWithSunIfNeedメソッドは太陽との距離が200以内であれば、太陽から遠ざかるための処理をおこないます。またその際に自機を攻撃可能であれば攻撃します。
太陽と充分離れている場合はなにも行なわずにfalseを返します。
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 |
public class EnemyThink { bool AvoidCollisionWithSunIfNeed() { double dx = Enemy.CenterX - Sun.Center.X; double dy = Enemy.CenterY - Sun.Center.Y; double distance = Math.Sqrt(Math.Pow(dx, 2) + Math.Pow(dy, 2)); if (distance < 200) { int bestAngle = GetIdealAngleToSun((int)Enemy.CenterX, (int)Enemy.CenterY); int ret = GetCorrectionAngle(Enemy.Angle, bestAngle); if (ret > 0) Enemy.Angle += 4; else Enemy.Angle -= 4; ret = GetCorrectionAngle(Enemy.Angle, bestAngle); if (Math.Abs(ret) < 20) Enemy.Accelerate(true); else Enemy.Accelerate(false); if (CanAttack()) Enemy.Shot(); return true; } return false; } } |
自機を攻撃可能であれば攻撃させる
CanAttackメソッドは自機を攻撃することが可能かどうかを調べて結果を返します。自機との距離が400より小さく角度のずれも30度より小さければ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 |
public class EnemyThink { bool CanAttack() { if (DistanceToJiki() < 400) { int angle = GetIdealAngleForJiki((int)Enemy.CenterX, (int)Enemy.CenterY); int correctionAngle = GetCorrectionAngle(Enemy.Angle, angle); if(Math.Abs(correctionAngle) < 30) return true; } return false; } int DistanceToJiki() { return (int)Math.Sqrt(Math.Pow(Enemy.CenterX - Jiki.CenterX, 2) + Math.Pow(Enemy.CenterY - Jiki.CenterY, 2)); } int GetIdealAngleForJiki(int x, int y) { double dx = x - Jiki.CenterX; double dy = y - Jiki.CenterY; double rad = Math.Atan2(dy, dx); double idealAngle = 180 * rad / Math.PI - 90; if (idealAngle < 0) idealAngle += 360; else if (idealAngle > 360) idealAngle -= 360; return (int)idealAngle; } } |
自機がいる方向に回頭させる
もし太陽から離れた場所であるなら自機を攻撃するために移動させます。ただしその通過点に太陽がある場合はそのまま追いかけることはできません。そこでそのまま追いかけても安全であるなら追いかけ、そうでないなら方向転換をするだけにとどめます。
IsSafeAngleForSunメソッドは引数の角度が太陽に対して安全な角度かどうかを返します。太陽に対して左右60度以上のズレがあれば安全であると判断します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class EnemyThink { bool IsSafeAngleForSun(int angle) { int idealAngle = GetIdealAngleToSun((int)Enemy.CenterX, (int)Enemy.CenterY); int correctionAngle = GetCorrectionAngle(angle, idealAngle); // 太陽を背にする角度から左右120度=太陽に対して左右60度以上のズレがあれば安全とする if (Math.Abs(correctionAngle) < 120) return true; else return false; } } |
TurnTowardsJikiメソッドは自機がいる方向に回頭する処理をおこないます。
太陽に対して安全な角度であれば加速させます。ただどこまでも加速してはゲームにならないので上限を設けています。また常に加速するわけではなく確率は2分の1です。太陽に対して安全な角度ではない場合は回頭するだけで加速はしません。ただし敵機が完全に停止した状態ではやはりゲームにならないので一定の速度までは加速させます。また攻撃可能であれば弾丸を発射させます。
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 |
public class EnemyThink { Random Random = new Random(); bool TurnTowardsJiki() { int angle = Enemy.Angle; int angle1 = GetIdealAngleForJiki((int)Enemy.CenterX, (int)Enemy.CenterY); int correctionAngle = GetCorrectionAngle(Enemy.Angle, angle1); if (correctionAngle > 0) Enemy.Angle += 2; else Enemy.Angle -= 2; if (IsSafeAngleForSun(angle)) { // 太陽に対して安全な角度 if (Enemy.Speed < 2 && Random.Next(2) == 0) Enemy.Accelerate(true); else Enemy.Accelerate(false); } else { if (Enemy.Speed < 0.5) Enemy.Accelerate(true); else Enemy.Accelerate(false); } if (CanAttack()) Enemy.Shot(); return true; } } |