質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

1回答

7132閲覧

ローカル座標とワールド座標のベクトルの変換

退会済みユーザー

退会済みユーザー

総合スコア0

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2019/04/24 18:34

編集2019/04/24 18:36

前提・実現したいこと

ローカル座標とワールド座標のベクトルの変換についてご教示お願いします。

・質問1。
下記は同じベクトルになりますか?

C#

1Vector3.forward; 2this.transform.InverseTransformVector(this.transform.forward);

・質問2。
下記は同じベクトルになりますか?

C#

1this.transform.forward 2this.transform.TransformVector(Vector3.forward)

・質問3
下記は同じコードになりますか?

C#

1this.transform.Translate(Vector3.forward * speed * Time.deltaTime); 2this.transform.Translate(this.transform.InverseTransformVector(this.transform.forward) * speed * Time.deltaTime);

・質問4
下記は同じコードになりますか?

C#

1this.transform.Translate(this.transform.forward * speed * Time.deltaTime, Space.World); 2this.transform.Translate(this.transform.TransformVector(Vector3.forward) * speed * Time.deltaTime, Space.World);

・質問5。
TransformVectorTransformDirectionの違いや、InverseTransformVectorInverseTransformDirectionの違いは何ですか?
ドキュメントを見てみると、formVectorの方がスケールの影響を受けて、
formDirectionはスケールの影響を受けないという違いに思えました。
このスケールの違いなのですが、formDirectionの方は変換した際に大きさが1に正規化されてしまうのかと
思ったのですが、その認識で合っていますか?
本当は、質問3と質問4も、Vector3.forwardやthis.transform.forwardのような大きさが1のベクトルではなく、
それ以外の大きさのベクトルで、ローカル座標とワールド座標のベクトルの変換を行って、
同じ移動になるか確かめたかったのですが、その検証でどういったベクトルを用意すればいいかわからなかったです。
ローカル座標のベクトルをワールド座標のベクトルに変換する場合、もしくはその逆の場合でも、
ベクトルを維持(方向と大きさを維持)して変換できるのは、formDirectionの方ではなく、formVectorの方ということで合っていますか?

試したこと

・質問1。
オブジェクトを傾けてログを取ったら、一応、同じ値を出力しました。

C#

1Debug.Log(Vector3.forward); 2Debug.Log(this.transform.InverseTransformVector(this.transform.forward));

・質問2。
オブジェクトを傾けてログを取ったら、一応、同じ値を出力しました。

C#

1Debug.Log(this.transform.forward); 2Debug.Log(this.transform.TransformVector(Vector3.forward))

・質問3と質問4。
それぞれ、同じ方向に進むように見えました。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

質問1、2、3、4については、リファレンスで言及されている「スケールの影響を受ける」という部分が効いてくるために、必ずしも同じになるとは限らないと思われます。
どうやらtransform.righttransform.uptransform.forwardは回転だけを考慮するようですので、後述しますがTransformDirectionInverseTransformDirectionの方ならば一致するんじゃないでしょうか。

質問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は平行移動だけを無視し、回転とスケール(さらに言うとスキュー...直方体を平行六面体にひしゃげさせる変形)を考慮する

といった差異があるんじゃないかと思いました。
実験をする場合、オブジェクトをわざと変な形にひしゃげさせてみるといいんじゃないでしょうか。
下図のようにキューブを変形させ...
キューブを変形
キューブに下記スクリプトをアタッチしてみますと...

C#

1using UnityEngine; 2 3public class TransformDirectionVsTransformVector : MonoBehaviour 4{ 5 private void Start() 6 { 7 // TransformDirectionはおそらく回転成分しか使用していないので... 8 var dx = this.transform.TransformDirection(Vector3.right); 9 var dy = this.transform.TransformDirection(Vector3.up); 10 var dz = this.transform.TransformDirection(Vector3.forward); 11 12 // 3つの基底の長さは1のまま維持され... 13 Debug.Log($"Direction X length:{dx.magnitude}"); 14 Debug.Log($"Direction Y length:{dy.magnitude}"); 15 Debug.Log($"Direction Z length:{dz.magnitude}"); 16 17 // 基底間の角度も90°のまま維持される 18 Debug.Log($"Direction X-Y angle:{Vector3.Angle(dx, dy)}"); 19 Debug.Log($"Direction X-Z angle:{Vector3.Angle(dy, dz)}"); 20 Debug.Log($"Direction Z-X angle:{Vector3.Angle(dz, dx)}"); 21 22 // 一方、TransformVectorはおそらく回転成分、スケール成分、スキュー成分が合成されるので... 23 var vx = this.transform.TransformVector(Vector3.right); 24 var vy = this.transform.TransformVector(Vector3.up); 25 var vz = this.transform.TransformVector(Vector3.forward); 26 27 // 3つの基底の長さは変形具合に応じて伸縮し... 28 Debug.Log($"Vector X length:{vx.magnitude}"); 29 Debug.Log($"Vector Y length:{vy.magnitude}"); 30 Debug.Log($"Vector Z length:{vz.magnitude}"); 31 32 // さらに、ひしゃげた変形がかかっている場合、基底同士が直交しなくなる 33 Debug.Log($"Vector X-Y angle:{Vector3.Angle(vx, vy)}"); 34 Debug.Log($"Vector X-Z angle:{Vector3.Angle(vy, vz)}"); 35 Debug.Log($"Vector Z-X angle:{Vector3.Angle(vz, vx)}"); 36 } 37 38 void Update() 39 { 40 var t = this.transform; 41 42 // ランダムな向き、0~10の範囲のランダムな大きさを持つVector3を作る 43 var localVector = Random.insideUnitSphere * 10.0f; 44 45 // 4種類の方法でローカルからワールドに変換する 46 47 // その1:回転だけを使って変換 48 var rot = t.rotation * localVector; 49 50 // その2:transformの変換行列を使って変換 51 // localVectorをVector4と見なして(wが0)変換するため、平行移動は無視され回転・スケール・スキューだけが影響する 52 var mat = (Vector3)(t.localToWorldMatrix * localVector); 53 54 // その3:TransformDirectionを使って変換 55 // Unityがどのように計算しているかは公開されていないが、おそらくrotと一致する 56 var tDir = t.TransformDirection(localVector); 57 58 // その4:TransformVectorを使って変換 59 // Unityがどのように計算しているかは公開されていないが、おそらくmatと一致する 60 var tVec = t.TransformVector(localVector); 61 62 // 結果を比較する 63 // 比較したい2つのベクトルの差をとり、その大きさがほぼ0なら一致している 64 var rotTDirMag = (rot - tDir).magnitude; // 一致するはず 65 var matTVecMag = (mat - tVec).magnitude; // 一致するはず 66 var rotMatMag = (rot - mat).magnitude; // 一致するとは限らないはず 67 var tDirTVecMag = (tDir - tVec).magnitude; // 一致するとは限らないはず 68 69 // 各階層のスケールがすべて(1, 1, 1)なら4つともほぼ0になるが、 70 // さもなければrot-tDirとmat-tVecしかほぼ0にならないはず 71 Debug.Log($"rot-tDir:{rotTDirMag:00.000000} mat-tVec:{matTVecMag:00.000000} rot-mat:{rotMatMag:00.000000} tDir-tVec:{tDirTVecMag:00.000000}"); 72 } 73} 74

微妙な誤差が生じましたが、だいたい想定通りの結果になりました。
結果

投稿2019/04/24 21:45

編集2019/04/24 22:28
Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2019/04/25 19:26

ご回答ありがとうございます。 スケールとは、オブジェクトのトランスフォームのスケールのことだったのですね。 ベクトルの大きさと勘違いしてました(それゆえ、正規化という発想になってしまいました)。 なるほど、~Directionの方が自分の意図したかったもののようです。 確認させていただきたいのですが、 下記2つが同じコード、 this.transform.forward this.transform.TransformDirection(Vector3.forward) そして、下記2つも同じコードという認識で合っていますか? Vector3.forward this.transform.InverseTransformDirection(this.transform.forward) 今回、このようなことに興味を持ったのは、 dir = (sphere.transform.position - this.transform.position); とワールド座標のベクトルを取得したとき、 this.transform.Translate(Vector3.Normalize(dir) * speed * Time.deltaTime, Space.World); とすべき所を誤って、 this.transform.Translate(Vector3.Normalize(dir) * speed * Time.deltaTime); としてしまい、意図した動作にならずハマってしまい、 上記の正しいコードで解決できたのですが、 このとき、TranslateがSpace.Selfでも、ワールド座標のベクトルdirを指定する形で書くことはできないかと思いました。 確認させていただきたいのですが、 下記2つは同じコードで合っていますか? this.transform.Translate(Vector3.Normalize(dir) * speed * Time.deltaTime, Space.World); this.transform.Translate(this.transform.InverseTransformDirection(Vector3.Normalize(dir)) * speed * Time.deltaTime); また、下記のようにローカル座標のベクトルで指定した場合も、 dir = this.transform.InverseTransformPoint(sphere.transform.position); 下記2つは同じコードで合っていますか? this.transform.Translate(Vector3.Normalize(dir) * speed * Time.deltaTime); this.transform.Translate(this.transform.TransformDirection(Vector3.Normalize(dir)) * speed * Time.deltaTime, Space.World); それぞれ、動かしてみて同一の動きをしているように見えましたが、間違っている点や注意点等ありましたら、ご教示お願いします。
Bongo

2019/04/25 20:35

まずtransform.forwardについては、https://github.com/Unity-Technologies/UnityCsReference/blob/master/Runtime/Transform/ScriptBindings/Transform.bindings.cs 内に... // The blue axis of the transform in world space. public Vector3 forward { get { return rotation * Vector3.forward; } set { rotation = Quaternion.LookRotation(value); } } との記述がありましたので、回答中で申し上げた「TransformDirectionは回転だけを考慮する」という予想が合っていれば「this.transform.forward」と「this.transform.TransformDirection(Vector3.forward)」は同等(実験結果ではわずかに誤差が発生しているため、まったく同じ計算プロセスを経ているのではなさそうでしたので「同じ」と言い切るのは避けました)、そしてその逆の「Vector3.forward」と「this.transform.InverseTransformDirection(this.transform.forward)」も同等と言ってよさそうです。 次に、ワールド座標系ベクトルを使いTranslateのrelativeToを省略してSpace.Selfで同等の動きをさせる、およびローカル座標系ベクトルを使いSpace.Worldで同等の動きをさせる件についてですが、これもどちらも問題なさそうです。 Transform.bindings.cs内では... // Moves the transform in the direction and distance of /translation/. public void Translate(Vector3 translation, [UnityEngine.Internal.DefaultValue("Space.Self")] Space relativeTo) { if (relativeTo == Space.World) position += translation; else position += TransformDirection(translation); } となっており、Space.Selfの場合は引数のtranslationをTransformDirectionで変換してpositionに加算しているようでした。ですので、おっしゃる通りtranslationをあらかじめTransformDirectionで変換してからTranslateをSpace.Worldで実行すればSpace.Selfの時と同等になるでしょうし、逆にあらかじめtranslationをInverseTransformDirectionで逆変換してからTranslateをSpace.Selfで実行すれば、translationがメソッド内のTransformDirectionによりワールド座標系のベクトルに変わり、Space.Worldの時と同等になると言えそうです。 ※最後に一つお詫びしますが、回答中のコードのStart内での実験の部分で、Y基底とZ基底のなす角度をコンソールに出力する部分の見出しが「Direction X-Z angle:」、「Vector X-Z angle:」になっていました。 実験自体に影響する部分ではないですが、正しい見出しは「Direction Y-Z angle:」、「Vector Y-Z angle:」となります。すみません...
退会済みユーザー

退会済みユーザー

2019/04/26 13:59

ご回答ありがとうございます。 すみません、Transform.bindings.cs内での記述を根拠にされてご回答いただいていたので、 気になってしまったのですが、 Translateに限らず、 一般的に、 dirが下記のようなワールド座標のベクトルの場合、 dir = sphere.transform.position - this.transform.position; 下記は同等のベクトルと言えますか? ワールド座標を基準した場合:dir thisのローカル座標を基準にした場合:this.transform.InverseTransformDirection(dir) 同様に、 dirが下記のようなローカル座標のベクトルの場合、 dir = this.transform.InverseTransformPoint(sphere.transform.position); 下記は同等のベクトルと言えますか? ワールド座標を基準にした場合:this.transform.TransformDirection(dir) thisのローカル座標を基準にした場合:dir 上記のことが言えれば、下記のような組み合わせもそれぞれ同等と言えると思うのですが、 いかがですか? dirが下記のようなワールド座標のベクトルの場合、 dir = sphere.transform.position - this.transform.position; 下記は同等のコードですか? rb.AddForce(Vector3.Normalize(dir)); rb.AddRelativeForce(this.transform.InverseTransformDirection(Vector3.Normalize(dir))); dirが下記のようなローカル座標のベクトルの場合、 dir = this.transform.InverseTransformPoint(sphere.transform.position); 下記は同等のコードですか? rb.AddForce(this.transform.TransformDirection(Vector3.Normalize(dir))); rb.AddRelativeForce(Vector3.Normalize(dir)); 見出しのご訂正ありがとうございます。
Bongo

2019/04/26 22:24 編集

鋭い着眼点かと思います。ややこしいことに、メッシュの描画などに関しては回答中で例示しましたひしゃげた座標系にも対応できて、キューブは正しくひずんだ形でレンダリングされますが(ただし、十分試したわけではないですが、もしかするとSkinnedMeshRendererの場合は怪しいかもしれません...少し動かしてみた限りでは目に見える異常はなさそうでしたが、ちょっと心配です)、ColliderやRigidbodyなど物理シミュレーションに関する領域ではそうではないようです。たとえば、ひずんだ座標系にあるrbに対してrb.AddRelativeForce(Vector3.one)を実行しても、飛んでいく向きは伸縮された平行六面体の対角線方向ではなく、各辺の長さが1の立方体(つまり、ギズモの表示モードを「Local」にしてオブジェクトを選択したときに表示される3色の矢印...長さが1で互いに直交したXYZ基底が作る立方体)の対角線方向になるようでした。 ですので、物理シミュレーションに関するローカル・ワールドの座標系変換は「~Direction」系の方が相性がいいでしょう。 AddForceなどの実装も非公開だったためはっきりとは分かりませんが、ご質問者さんがコメント後半でおっしゃった rb.AddForce(Vector3.Normalize(dir)); rb.AddRelativeForce(this.transform.InverseTransformDirection(Vector3.Normalize(dir))); と rb.AddForce(this.transform.TransformDirection(Vector3.Normalize(dir))); rb.AddRelativeForce(Vector3.Normalize(dir)); についてはおそらくそれぞれ同等と言えるように思います。 しかし前半については、物理シミュレーション以外の文脈では問題がありそうですね(スケールがすべて1なら大丈夫でしょうが)。こちらのケースでは逆に回転以外も考慮する「~Vector」系の方が相性がいいでしょう。 まず、 dir = sphere.transform.position - this.transform.position; ワールド座標を基準した場合:dir thisのローカル座標を基準にした場合:this.transform.InverseTransformDirection(dir) ですが、ここでthis.transformの子階層に適当なオブジェクト...仮にcapsuleを置いたとして、 capsule.transform.localPosition = this.transform.InverseTransformDirection(dir); としてもcapsuleの位置はsphereとは重ならないんじゃないでしょうか? capsule.transform.localPosition = this.transform.InverseTransformVector(dir); ならスケールも考慮されるので重なってくれるかと思います。 同じく、 dir = this.transform.InverseTransformPoint(sphere.transform.position); ワールド座標を基準にした場合:this.transform.TransformDirection(dir) thisのローカル座標を基準にした場合:dir でも、 capsule.transform.position = this.transform.position + this.transform.TransformDirection(dir); では重ならないかもしれませんが capsule.transform.position = this.transform.position + this.transform.TransformVector(dir); ならうまくいくはずです。 何だかややこしい説明になってしまいすみません。拡縮(特にXYZ不均等の)が絡むと状況がやっかいになってしまいますね。 https://docs.unity3d.com/ja/current/Manual/Transforms.html の「パフォーマンスの問題と不均等スケーリングの制約」の節でも、不均等スケーリングの注意点についてちょこっと言及されていました。実際のゲーム製作においては、ひずんだ座標系は可能な限り避けた方が無難なように思います。 理想的にはXYZ軸は常に直交してスケールは1倍(拡縮するにしてもXYZすべて均等に)を維持したいところですが、スケールのXYZに異なる値を入れるのもある程度は必要な場面があるでしょう。ですが、さらにその子階層にオブジェクトを入れて、しかもそれが回転していたりすると特に注意が必要でしょうね。
退会済みユーザー

退会済みユーザー

2019/04/27 00:16

ご回答ありがとうございます。 なるほど、物理シミュレーションに関するローカル・ワールドの座標系変換は 「~Direction」系の方がよいのですね。 前半に関しては、物理シミュレーションで扱う場合は同等であるけれども、 スケールが1以外で回転以外も考慮する場合は、「~Vector」系の方で、同等ということですね。 ご提示のURL拝見しました。 XYZ不均等とは、 Scaleが例えばxyz=(2, 4, 2)のような状態のことなのですね。 とても勉強になりました。 ご教示いただきありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問