偏差射撃(Unity制作)
Unityで偏差射撃
Unityで初めて制作したゲームをうに部屋さんに置かせてもらっています。
さて、このゲームでは射撃をしてくる砲台が設置してありますが、そのAIは至極単純なものです。
- 自分を中心とした球状の一定範囲内に自機が居る時
- 自分の正面から一定角度に自機が居る時
上記の条件を満たした時に自機の方へ向くように回転させ射撃を行う
です。
ですがこれにはまだ問題があります。
ミサイルなどの射撃後誘導する攻撃ならばいいのですが、普通の弾丸はまっすぐ飛んでいきます。
当然着弾までには時間がかかり、その間に自機が移動していれば、発射の瞬間に砲台が狙っていた場所にはすでに自機は存在しておらず命中しません。
動いているターゲットに弾丸を命中させるためには、その未来位置に向かって射撃する必要があります。これを偏差射撃といいます(他にもいろんな言い方はありますが)
偏差射撃に必要な要素
ではその偏差射撃はどうすれば出来るのか。必要な要素は以下のとおりです
- ターゲットの移動速度
- 発射点とターゲットとの距離
- 発射する弾丸の速度
ターゲットの移動速度について
ターゲットの移動速度を算出するには2つのVector3変数を用意しました。適当につけた変数名ですので参考までにということで。「target」は索敵範囲内に入った対象のオブジェクトのことを示しているので、適当な方法でオブジェクトを取得しておいてあげてください。
- MoveSpeedNow:Vector3
ターゲットの移動速度(ベクトル)です。ターゲットの現在位置から1フレーム直前の過去位置を引いた値になります。
MoveSpeedNow = target.transform.position - MoveSpeedBefore;
- MoveSpeedBefore:Vector3
ターゲットの過去位置です。処理時のターゲット位置を入れます。
MoveSpeedBefore = target.transform.position;
発射点とターゲットとの距離について
変数を用意します。
- TargetDistance(float)
Vector3.Distance(A,B);でA地点からB地点の距離を算出してくれます。
TargetDistance = Vector3.Distance(this.transform.position,target.position);
発射する弾丸の速度について
弾丸を飛ばす方法には色々とありますが、自分の場合は以下の方法で行っています。
弾丸を生成した瞬間にその弾丸に攻撃力や弾速などのステータスを送り込み、その際に物理的な力を加えて前方に弾き飛ばす。
GetComponent.<Rigidbody>().AddForce(transform.forward * speed,ForceMode.Impulse);
弾丸は自らのリジッドボディコンポーネントを取得し、前方に向かって、与えられたspeedのステータス分だけ、ImpulseのForceModeで物理的力を与える。と言った感じの処理ですね。ForceMode.Impulseであることは重要です。
さてこのspeedがそのまま弾丸の速度としては扱えません。
弾丸の速度として使うには、リジッドボディのMassの値で割ってやる必要があります。ゲットコンポーネントで取得するなり、弾丸のリジッドボディのMassなんていじらねーから定数でやるぜ〜なり。
未来位置を算出する
さて、これで必要な要素がわかりましたので、ターゲットの未来位置を算出することが可能になります。
MoveSpeedAfter:Vector3
とでも名づけておきましょうか。
弾丸のスピードと、相手までの距離がわかれば、小学校の計算で相手までの飛翔時間が出せます。時間=距離÷速さ ですね。
後はこの飛翔時間を、ターゲットの移動速度ベクトルにかけ、ターゲットの現在位置に足してやれば、ターゲットの未来位置が算出できます。
が、この飛翔時間の単位は「秒」なので単純に移動速度ベクトルにかけてもうまく動作しません。「秒」を1フレームで処理する値に分割するため、前フレームからの経過時間を格納しているTime.deltaTimeで割ってやりましょう。
MoveSpeedAfter = target.position + MoveSpeedNow * TargetDistance/(speed/0.5)/Time.deltaTime;
0.5となっているのはMassの値です(めんどくさいので固定値入れてます)
これで未来位置が取れたので、後は射撃する砲台の向きをこの未来位置に向けてあげればOKです。
まとめ
MoveSpeedNow = target.transform.position - MoveSpeedBefore;
MoveSpeedBefore = target.transform.position;
TargetDistance = Vector3.Distance(this.transform.position,target.position);
MoveSpeedAfter = target.position + MoveSpeedNow * TargetDistance/(speed/0.5)/Time.deltaTime;
偏差射撃はこのようにして実装しました。(上記ゲーム中では適用させていませんが)
この偏差射撃もまだ問題点があります。弾丸の飛翔時間は現在地同士で測定するため、未来位置までの飛翔時間が現在地と比べて変動する場合、命中率が低下します。
敵が行ってくる偏差射撃としてはゲームバランス的にそれほど問題が出てないので気にはしてませんけども(適度に当たらないほうがプレイヤーには有益なため)、できれば後々改良したいところであります。にしても変数名が適当すぎる。
*1:UnityのJavaScriptの書き方で解説していますのでC#とかの場合は脳内変換でお願いします