Physics.LinecastやPhysics.Raycastでレイを飛ばした時に、RaycastHit型の変数に接触した相手の情報が入ります。
この情報を使って接触面の角度を計算し、その角度に合わせて弾痕を接触面と同じ角度に合わせて表示したり、
キャラクターの足の角度を接触面と合わせるという事を行ってきました。
角度を計算する為に
Quaternion.FromToRotationを使っていますが、このFromToRotationは2つの方向からその角度を計算出来る関数です。
その為、例えば銃から弾を撃って壁に接触した場合は『弾の進む方向』と『壁の接触面の方向』を指定しています。
弾の進む方向は銃口の先を指定すればいいので銃口のtransform.forwardを指定します。
一方、壁の接触面の方向にはRaycastHitのnormalプロパティを指定しています。
このRaycastHitのnormalプロパティとはなんなんでしょうか?
Unityのスクリプトリファレンスを見てみると
衝突した際の面の角度
となっています。
しかし、RaycastHit.normalで得られる値はVector3型の値です。
角度と言っても方向で返ってくるんですね・・・・(^_^;)
というわけで少し混乱する事になったので、サンプルを使ってRaycastHit.normalで返ってくる値はなんなのかを調べてみましょう。
2つのCubeを作り片方からレイを飛ばして検証する
サンプルのCubeを作成して設置
まずレイが衝突される側のゲームオブジェクトをCubeで作成します。
位置や角度はそのままでレイヤーにBlockを作り設定を変更しておきます。
次にレイを飛ばす側のCubeを作成します。
↑のようにZ方向の位置を変更し、Y軸の角度を180度回転させ先ほど作ったCubeと向い合せになるようにします。
新しくFromToRotationTestというスクリプトを作成し取り付けておきます。
スクリプトの中身は後で作成します。
↑が実際の画面になります。
赤矢印の方向にレイを飛ばし、レイを飛ばした方向と衝突面から角度を計算します。
レイを飛ばして衝突面の角度を出力するFromToRotationTestスクリプトの作成
それではFromToRotationTestスクリプトの中身を記述していきましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | void Update () { Debug.DrawLine(transform.position, transform.position + transform.forward * 3f, Color.red); if(Input.GetKeyDown("space")) { RaycastHit hit; if(Physics.Linecast(transform.position, transform.position + transform.forward * 3f, out hit, LayerMask.GetMask("Block"))) { // 衝突した面が向いている方向のベクトルをVector3で出力 Debug.Log(hit.normal); // 衝突した面の前方方向と衝突した面の方向から角度を算出し確認 Debug.Log(Vector3.Angle(hit.transform.forward, hit.normal)); // レイを飛ばした方向と衝突した面の間の角度を算出する Debug.Log(Quaternion.FromToRotation(transform.forward, hit.normal).eulerAngles); } } } |
Updateメソッド内でspaceキーが押された時にレイを前方に飛ばします。
レイを視覚化してわかりやすいようにDebug.DrawLineを使って表示しています。
レイはレイを飛ばすゲームオブジェクトの位置から前方に3m先まで飛ばし、ぶつかった相手のレイヤーがBlockである時にif文内を実行します。
ぶつかった相手の情報はhitに格納されます。
1 2 3 | Debug.Log(hit.normal) |
でhit.normalの値そのものを出力します。
1 2 3 | Debug.Log(Vector3.Angle(hit.transform.forward, hit.normal)); |
で衝突したゲームオブジェクトの前方とhit.normalの角度を計算し出力しています。
これはhit.normalが衝突した面の方向を表しているのかどうかを確認する為に、衝突したゲームオブジェクトの前方との角度を計算して出力するので、
回転をさせていない場合は0が出力されるはずです。
Debug.Log(Quaternion.FromToRotation(transform.forward, hit.normal).eulerAngles);
でレイを飛ばした方向と衝突面の方向から角度を計算し、そのオイラー角(0から360度で表される角度)で出力しています。
衝突される側のCubeを回転し衝突面の角度を確認してみる
ではUnityの実行ボタンを押して確認してみましょう。
衝突される側のCubeを回転せずそのままレイを当てた場合
まずはCubeを回転はせずそのままレイを飛ばして確認してみます。
↑が結果です。
hit.normalはVector3(0, 0, 1)が返ってきています。
これは衝突面の前方向が出力されています。
その為、次の『衝突されるゲームオブジェクトの前方』と『衝突面の方向』の角度を計算すると0が返ってきています。
最後に衝突面の角度をオイラー角度で出力した値はVector3(0, 180, 180)が返ってきています。
これはレイを飛ばすCubeと衝突されるCubeが反対方向を向いているのでX軸以外は180となっています。
衝突される側のCubeを回転してレイを当てた場合
次に衝突される側のCubeのX軸の角度を変更して確認してみます。
↑のようにX軸の角度を50に変更します。
↑のように衝突される側のCubeが回転しています。
実行ボタンを押して確認してみましょう。
hit.normalはVector3(0.0, 0.6, 0.8)と衝突した面の方向が出力されます。
次の衝突面と衝突されたゲームオブジェクトの前方との角度を出力した場合は90度が返ってきています。
先ほどのゲームオブジェクトを回転した図を見て頂くとわかりますが、衝突面の角度(前方)は緑の矢印の方向で、衝突されたゲームオブジェクトの前方は青矢印の方向です。
その間の角度を計算すると90度になりますね。
最後のレイを飛ばした方向と衝突面の角度(方向)から計算した角度はVector3(40, 180, 180)となります。
この結果からも衝突面の角度であるRaycastHit.normalというのは衝突面が向いている方向が得られるという事ですね。
図にすると↑のような感じになります。
レイを飛ばすゲームオブジェクトからレイを飛ばした方向がtransform.forwardで衝突した面が向いている方向がhit.normal、その間の角度をQuaternion.FromToRotationで
求めているという感じになります。
図で見ると2Dの平面になっていますが、実際の角度は3Dの立体で計算された値が求められます。
hit.normalやQuaternion.FromToRotationについて調べた理由
RaycastHit.normalとかQuaternion.FromToRotation?
そんなの知ってるわ!
という方も多いかもしれませんが、個人的に理解しないまま使っていたので今回の記事で学習出来てよかったです(^_^)v
実は最初の方でもう答えが出てるんですけどね・・・・、Quaternion.FromToRotationで与える引数は方向(Vector3)の値であるという点で・・・。
しかしスクリプトマニュアルで衝突面の角度と表現されてる事や、得られる値がVector3だという事で個人的に混乱していたので、今回の記事を作成しました。
これで、次からちゃんと理解して使えます(^_^)v
Unityのゲームを作る際に角度を求める事って結構出てくるので位置、方向、角度の使い方をマスターすると役に立つと思います。