質問: ### 前提・実現したいこと ローカル座標とワールド座標のベクトルの変換についてご教示お願いします。 ・質問1。 下記は同じベクトルになりますか? ``` Vector3.forward; this.transform.InverseTransformVector(this.transform.forward); ``` ・質問2。 下記は同じベクトルになりますか? ``` this.transform.forward this.transform.TransformVector(Vector3.forward) ``` ・質問3 下記は同じコードになりますか? ``` this.transform.Translate(Vector3.forward * speed * Time.deltaTime); this.transform.Translate(this.transform.InverseTransformVector(this.transform.forward) * speed * Time.deltaTime); ``` ・質問4 下記は同じコードになりますか? ``` this.transform.Translate(this.transform.forward * speed * Time.deltaTime, Space.World); this.transform.Translate(this.transform.TransformVector(Vector3.forward) * speed * Time.deltaTime, Space.World); ``` ・質問5。 TransformVectorとTransformDirectionの違いや、InverseTransformVectorとInverseTransformDirectionの違いは何ですか? ドキュメントを見てみると、formVectorの方がスケールの影響を受けて、 formDirectionはスケールの影響を受けないという違いに思えました。 このスケールの違いなのですが、formDirectionの方は変換した際に大きさが1に正規化されてしまうのかと 思ったのですが、その認識で合っていますか? 本当は、質問3と質問4も、Vector3.forwardやthis.transform.forwardのような大きさが1のベクトルではなく、 それ以外の大きさのベクトルで、ローカル座標とワールド座標のベクトルの変換を行って、 同じ移動になるか確かめたかったのですが、その検証でどういったベクトルを用意すればいいかわからなかったです。 ローカル座標のベクトルをワールド座標のベクトルに変換する場合、もしくはその逆の場合でも、 ベクトルを維持(方向と大きさを維持)して変換できるのは、formDirectionの方ではなく、formVectorの方ということで合っていますか? ### 試したこと ・質問1。 オブジェクトを傾けてログを取ったら、一応、同じ値を出力しました。 ``` Debug.Log(Vector3.forward); Debug.Log(this.transform.InverseTransformVector(this.transform.forward)); ``` ・質問2。 オブジェクトを傾けてログを取ったら、一応、同じ値を出力しました。 ``` Debug.Log(this.transform.forward); Debug.Log(this.transform.TransformVector(Vector3.forward)) ``` ・質問3と質問4。 それぞれ、同じ方向に進むように見えました。
回答: 質問1、2、3、4については、リファレンスで言及されている「スケールの影響を受ける」という部分が効いてくるために、必ずしも同じになるとは限らないと思われます。 どうやら`transform.right`、`transform.up`、`transform.forward`は回転だけを考慮するようですので、後述しますが`TransformDirection`や`InverseTransformDirection`の方ならば一致するんじゃないでしょうか。 質問5についてですが、`TransformDirection`に大きさを正規化する機能はなさそうです。リファレンスには... > > This operation is not affected by scale or position of the transform. The returned vector has the same length as direction. > > > *この操作はtransformのスケールや位置の影響を受けません。返されるベクトルはdirectionと同じ長さとなります。* とあり、大きさは変換前と同じになるそうです(もし正規化されるのなら、変換後のベクトルの大きさは常に1となるのではないでしょうか)。 `~Vector`と`~Direction`の違いについて試してみたところ、おそらく... * `~Direction`は回転だけを考慮する * `~Vector`は平行移動だけを無視し、回転とスケール(さらに言うとスキュー...直方体を平行六面体にひしゃげさせる変形)を考慮する といった差異があるんじゃないかと思いました。 実験をする場合、オブジェクトをわざと変な形にひしゃげさせてみるといいんじゃないでしょうか。 下図のようにキューブを変形させ... !キューブを変形 キューブに下記スクリプトをアタッチしてみますと... ``` using UnityEngine; public class TransformDirectionVsTransformVector : MonoBehaviour { private void Start() { // TransformDirectionはおそらく回転成分しか使用していないので... var dx = this.transform.TransformDirection(Vector3.right); var dy = this.transform.TransformDirection(Vector3.up); var dz = this.transform.TransformDirection(Vector3.forward); // 3つの基底の長さは1のまま維持され... Debug.Log($"Direction X length:{dx.magnitude}"); Debug.Log($"Direction Y length:{dy.magnitude}"); Debug.Log($"Direction Z length:{dz.magnitude}"); // 基底間の角度も90°のまま維持される Debug.Log($"Direction X-Y angle:{Vector3.Angle(dx, dy)}"); Debug.Log($"Direction X-Z angle:{Vector3.Angle(dy, dz)}"); Debug.Log($"Direction Z-X angle:{Vector3.Angle(dz, dx)}"); // 一方、TransformVectorはおそらく回転成分、スケール成分、スキュー成分が合成されるので... var vx = this.transform.TransformVector(Vector3.right); var vy = this.transform.TransformVector(Vector3.up); var vz = this.transform.TransformVector(Vector3.forward); // 3つの基底の長さは変形具合に応じて伸縮し... Debug.Log($"Vector X length:{vx.magnitude}"); Debug.Log($"Vector Y length:{vy.magnitude}"); Debug.Log($"Vector Z length:{vz.magnitude}"); // さらに、ひしゃげた変形がかかっている場合、基底同士が直交しなくなる Debug.Log($"Vector X-Y angle:{Vector3.Angle(vx, vy)}"); Debug.Log($"Vector X-Z angle:{Vector3.Angle(vy, vz)}"); Debug.Log($"Vector Z-X angle:{Vector3.Angle(vz, vx)}"); } void Update() { var t = this.transform; // ランダムな向き、0~10の範囲のランダムな大きさを持つVector3を作る var localVector = Random.insideUnitSphere * 10.0f; // 4種類の方法でローカルからワールドに変換する // その1:回転だけを使って変換 var rot = t.rotation * localVector; // その2:transformの変換行列を使って変換 // localVectorをVector4と見なして(wが0)変換するため、平行移動は無視され回転・スケール・スキューだけが影響する var mat = (Vector3)(t.localToWorldMatrix * localVector); // その3:TransformDirectionを使って変換 // Unityがどのように計算しているかは公開されていないが、おそらくrotと一致する var tDir = t.TransformDirection(localVector); // その4:TransformVectorを使って変換 // Unityがどのように計算しているかは公開されていないが、おそらくmatと一致する var tVec = t.TransformVector(localVector); // 結果を比較する // 比較したい2つのベクトルの差をとり、その大きさがほぼ0なら一致している var rotTDirMag = (rot - tDir).magnitude; // 一致するはず var matTVecMag = (mat - tVec).magnitude; // 一致するはず var rotMatMag = (rot - mat).magnitude; // 一致するとは限らないはず var tDirTVecMag = (tDir - tVec).magnitude; // 一致するとは限らないはず // 各階層のスケールがすべて(1, 1, 1)なら4つともほぼ0になるが、 // さもなければrot-tDirとmat-tVecしかほぼ0にならないはず Debug.Log($"rot-tDir:{rotTDirMag:00.000000} mat-tVec:{matTVecMag:00.000000} rot-mat:{rotMatMag:00.000000} tDir-tVec:{tDirTVecMag:00.000000}"); } } ``` 微妙な誤差が生じましたが、だいたい想定通りの結果になりました。 !結果