
どうも、最近ますます腰痛がひどい右近です。
今回は前回予告していたとおり、「誘導ミサイル」の作り方を紹介したいと思います。
(前回の記事はこちら)
※例によって「神動画」を参考にしていますが、私の残念なお頭では「バネトルク」なるものが理解できなかったので、
若干、というかだいぶ動画とは違う実装方法に仕上がっております。
(神動画はこちら)
※使用バージョン:Unity 2018.2.9f1
この記事の目次
1. 今回紹介する「誘導ミサイル」の仕様
今回紹介する「誘導ミサイル」の仕様は以下の通りです。
・仕組みとしては前回紹介した「ホーミングレーザー」と同じ。
・当たるまで追尾しない(一回ターゲットを通り過ぎるとそのままどこかへ飛んでいく)。
・正面となる軸を指定する必要がある(後述)
一口に「誘導ミサイル」といっても色々な種類があると思うので、一応仕様を書いてみました。
2.誘導ミサイルの作り方
ここからは、それはもうザックリと作り方を紹介していきます。
ザックリ作業手順
1.「ターゲット」と「ミサイル」となるオブジェクトを設置。
2.ミサイル用スクリプトを作成し、ミサイルにアタッチ。ついでにRigidbodyもアタッチ。
1.「ターゲット」と「ミサイル」となるオブジェクトを設置
まずは、適当な位置に「ターゲット」と「ミサイル」となるオブジェクトを設置します。
「ターゲット」は3Dオブジェクトの「Cube」で名前を「mato」に、
「ミサイル」は3Dオブジェクトの「Capsule」で名前を「HomingMissile」とします。
2.ミサイル用スクリプトを作成し、ミサイルにアタッチ。ついでにRigidbodyもアタッチ。
次に「HomingMissile」という名前でC#スクリプトを作成します。
↓のコードをマルコピしてください。
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 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class HomingMissile : MonoBehaviour { //Rigidbodyを入れる変数 Rigidbody rigid; //速度 Vector3 velocity; //発射するときの初期位置 Vector3 position; // 加速度 public Vector3 acceleration; // ターゲットをセットする public Transform target; // 着弾時間 float period = 2f; // 正面とする軸を指定する public string Axis = "z"; // 正面とする軸 Vector3 frontAxis; void Start () { // 初期位置をposionに格納 position = transform.position; // rigidbody取得 rigid = this.GetComponent<Rigidbody>(); } void Update() { acceleration = Vector3.zero; //ターゲットと自分自身の差 var diff = target.position - transform.position; //加速度を求めてるらしい acceleration += (diff - velocity * period) * 2f / (period * period); //加速度が一定以上だと追尾を弱くする if (acceleration.magnitude > 100f) { acceleration = acceleration.normalized * 100f; } //着弾時間を徐々に減らしていく period -= Time.deltaTime; Debug.Log(period); //移動処理 velocity += acceleration * Time.deltaTime; } void FixedUpdate() { // 着弾時間内はターゲットを向くようにする if (period >= 0) { // 自分とターゲットとのベクトルを計算 var diff = target.position - transform.position; //どの軸を正面として進むか決める処理 switch (Axis) { case "x": frontAxis = transform.right; break; case "y": frontAxis = transform.up; break; case "z": frontAxis = transform.forward; break; default: Debug.Log("Axisに「x」、「y」、「z」のいずれかを指定してください"); break; } // Axisで指定した軸を正面にしてターゲットに向きを変える transform.rotation = GetRotMat(frontAxis, diff) * transform.rotation; } // 移動処理 rigid.MovePosition(transform.position + velocity * Time.deltaTime); } // aをbに向ける四元数を返す関数 static Quaternion GetRotMat(Vector3 a, Vector3 b) { a.Normalize(); b.Normalize(); float dot = Vector3.Dot(a, b); float rotateRad = Mathf.Acos(dot); if (rotateRad > 0.01) { Vector3 n = Vector3.Cross(a, b).normalized; return Quaternion.AngleAxis(Mathf.Rad2Deg * rotateRad, n); } else { return Quaternion.identity; } } } |
少しクセのある仕上がりとなっているので、スクリプトの説明をさせてください。
(update関数内の処理と一部変数については前回紹介した「ホーミングレーザー」と同じなので割愛します)
まずは↓の部分についてです。
1 2 3 4 |
// 正面とする軸を指定する public string Axis = "z"; // 正面とする軸 Vector3 frontAxis; |
この2つの変数はどの軸を正面としてターゲットに突っ込んでいくか決めるための変数になります。
(軸とはx軸、y軸、z軸のことです。Scene画面でオブジェクトを選択すると表示される赤、緑、青の矢印と言ったらほうがわかりやすいでしょうか?)
「Axis」にはインスペクター上で「x、y、z」のどれかを入力し、入力した軸を正面にぶっ飛んでいきます。
「frontAxis」には後で詳しく説明します。
なぜ、正面とする軸を決める必要があるのかというと、軸を決めないとロケットのように飛んでいかないからです!
とりあえずこれを見てください。
軸を決めずに飛ばすと、おそらくz軸を正面にして飛んでいくことだろうと思います。
(まあ、書き方にもよりますが・・・。)
しかし、見てもらったのでわかると思いますが、ロケットならz軸じゃなくて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 |
void FixedUpdate() { // 着弾時間内はターゲットを向くようにする if (period >= 0) { // 自分とターゲットとのベクトルを計算 var diff = target.position - transform.position; //どの軸を正面として進むか決める処理 switch (Axis) { case "x": frontAxis = transform.right; break; case "y": frontAxis = transform.up; break; case "z": frontAxis = transform.forward; break; default: Debug.Log("Axisに「x」、「y」、「z」のいずれかを指定してください"); break; } // Axisで指定した軸を正面にしてターゲットに向きを変える transform.rotation = GetRotMat(frontAxis, diff) * transform.rotation; } // 移動処理 rigid.MovePosition(transform.position + velocity * Time.deltaTime); } // aをbに向ける四元数を返す関数 static Quaternion GetRotMat(Vector3 a, Vector3 b) { a.Normalize(); b.Normalize(); float dot = Vector3.Dot(a, b); float rotateRad = Mathf.Acos(dot); if (rotateRad > 0.01) { Vector3 n = Vector3.Cross(a, b).normalized; return Quaternion.AngleAxis(Mathf.Rad2Deg * rotateRad, n); } else { return Quaternion.identity; } } |
ちょっとあれなので、すごくザックリいきます。
まず最初のif文。
これは、誘導ミサイルがいつまでもターゲットの方向を向かないよう、
着弾時間を過ぎたらターゲットの方向を向く処理を行わないようにするためのif文です。
つまり、if文の中ではターゲットの方向に向ける処理を行っています。
具体的にどんな処理を行っているかというと、まず自分とターゲットとの差分を計算し、適当な変数にぶち込んでいます。
その後、switch文で「Axis」の値によって、「frontAxis」に何を代入するかを決めています。
(ちなみに、「Axis」の値が「x、y、z」のどれでもないときはログで気づけるようにはなっているはずです。)
「frontAxis」の値を決めた後は、「GetRotMat」関数を呼び出したときの戻り値に、
「transform.rotation」を掛けた値を「transform.rotation」に代入することによって、
いい感じに指定した軸を正面としてターゲットの方向を向くらしいです。
(この処理は↓のサイト様を参考にさせていただきました。ありがとうございます)
後は、移動処理を行っているだけになります。
ザックリ説明終わり。
次は、今作った「HomingMissile」スクリプトを「HomingMissile」オブジェクトにアタッチします。
(※「Target」には的となるオブジェクトを、「Axis」には正面としたい軸を「x、y、z」で指定してください)
そして、「Rigidbody」もアタッチしましょう。
(※「Use Gravity」のチェックは外します。)
以上で誘導ミサイルを飛ばす準備は整いました。
後は実行して飛ばしましょう!
3.おわりに
今回はだいぶザックリとですが、誘導ミサイルの作り方を紹介しました。
もし、聞きたいことがある人はお気軽にコメントしてください。
答えられる範囲で答えさせていただきます。
それでは、最後まで見ていただきありがとうございました。