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

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

ただいまの
回答率

88.77%

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

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,520

tkmnusr

score 320

前提・実現したいこと

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

・質問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。
TransformVectorTransformDirectionの違いや、InverseTransformVectorInverseTransformDirectionの違いは何ですか?
ドキュメントを見てみると、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。
それぞれ、同じ方向に進むように見えました。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

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

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

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}");
    }
}


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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/04/26 22: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));

    見出しのご訂正ありがとうございます。

    キャンセル

  • 2019/04/27 07:20 編集

    鋭い着眼点かと思います。ややこしいことに、メッシュの描画などに関しては回答中で例示しましたひしゃげた座標系にも対応できて、キューブは正しくひずんだ形でレンダリングされますが(ただし、十分試したわけではないですが、もしかすると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 09:16

    ご回答ありがとうございます。
    なるほど、物理シミュレーションに関するローカル・ワールドの座標系変換は
    「~Direction」系の方がよいのですね。

    前半に関しては、物理シミュレーションで扱う場合は同等であるけれども、
    スケールが1以外で回転以外も考慮する場合は、「~Vector」系の方で、同等ということですね。

    ご提示のURL拝見しました。
    XYZ不均等とは、 Scaleが例えばxyz=(2, 4, 2)のような状態のことなのですね。

    とても勉強になりました。
    ご教示いただきありがとうございました。

    キャンセル

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

  • ただいまの回答率 88.77%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る