UnityのTransformのワールド空間とローカル空間について

今回はUnityのTransformのワールド空間とローカル空間の違いについて見ていきたいと思います。

UnityのゲームオブジェクトはインスペクタのTransformでPosition、Rotation、Scaleの値を見る事が出来ます。

Unityを使い始めの頃はPositionで言うと(0, 0, 0)を基準点とした数値だと思っていました。

しかしゲームオブジェクトは他のゲームオブジェクトの子要素として配置する事が出来、子要素のゲームオブジェクトは(0, 0, 0)を基準としたPositionにはなっていません。

子要素のゲームオブジェクトのPositionは親のゲームオブジェクトのPositionからの相対値が表示されています。

そこで今回は親のゲームオブジェクトと子のゲームオブジェクトを使って実験し、違いを確認してみたいと思います。

スポンサーリンク
スポンサーリンク

親と子のTransform情報の確認

ヒエラルキー上で右クリックし3D Object→Cubeを作成し、名前をParentとします。

その子要素にCubeを作成し名前をChildとします。

親と子のゲームオブジェクトを作成

ChildをParentの子要素にドラッグ&ドロップし移動させます。

親と子のインスペクタ

親と子のゲームオブジェクトのインスペクタでPosition、Rotation、Scaleを↑のようにまったく同じ数値を設定します。

親と子のゲームオブジェクトの絵

すると同じ数値を設定したにもかかわらず↑のように位置や角度、大きさが変わりました。

右上が子要素であるChildゲームオブジェクトですが、ChildのTransform情報はParentからの相対値になっている事がなんとなくわかると思います。

ワールド空間とローカル空間

子のゲームオブジェクトのTransform情報は親のゲームオブジェクトからの相対値という事が確認出来ました。

親を持たないゲームオブジェクト(親)のPositionやRotation、Scaleはワールド空間になります。

親を持つ子のゲームオブジェクトは親からの相対値で表示されるのでローカル空間になります。

ワールド空間とローカル空間の表示の仕方

Unityで位置や角度、サイズを扱う時にもワールド空間でのデータ、ローカル空間でのデータと取得の仕方を変更する事が出来ます。

↑のようにゲームオブジェクトのワールド空間での情報、ローカル空間での情報を取得出来ます。

ワールド、ローカル値を両方表示してみる

さきほどの親と子のゲームオブジェクトの親にスクリプトを取り付けどのようなデータが出力されるか確認してみます。

スクリプトの名前はWorldLocalという名前にします。

↑のようにpublicで親のゲームオブジェクトと子のゲームオブジェクトをインスペクタで設定出来るようにし、それぞれのワールド、ローカルでの値を表示しています。

ワールド空間とローカル空間のデータを表示

親のゲームオブジェクトは自身の親がないのでワールドでもローカルでも表示は同じです。

子のゲームオブジェクトはワールドの場合はインスペクタで表示されているデータではなくワールド値が表示されています。

ローカルの場合はインスペクタで表示されているデータと同じですね。

ワールド空間とローカル空間の変換

ワールド空間とローカル空間の表示の仕方がわかりました。

次に、ワールド空間の値とローカル空間の値を変換出来るようにしてみましょう。

例えば子のゲームオブジェクトが向いている方向に移動させたいと思います。

親のゲームオブジェクトであればtransform.forwardが前方を指しますのでこのプロパティを使えば済みます。

実は子のゲームオブジェクトでもtransform.forwardを使用すればワールド空間での方向が取得出来るので問題はありませんが・・・、

場合によっては子のゲームオブジェクトのローカルの前方を取得したい事があります。

例えが浮かばない・・・・(^_^;)

そんな時にワールドとローカルの相互変換が出来ると便利です。

引数として渡すのは全てVector3の値で戻り値も全てVector3の値が返ってきます。

これらの処理は実に混乱しやすい処理になります。(^_^;)

サンプル1

サンプルを作成し実行してみます。

↑のようなサンプルスクリプトを作成し親と子両方のゲームオブジェクトに設定します。

child.forwardで子の向いているワールドの方向、child.positionでワールド位置を取得出来ます。

それらを設定したスクリプトのゲームオブジェクトから見た変換を行います。

サンプルを実行する前に親と子のデータをわかりやすいものに変えます。

親のPositionは(0, 0, 0)、Rotationは(0, 0, 0)、Scaleは(1, 1, 1)に設定します。
子のPositionは(1, 1, 1)、Rotationは(0, 45, 0)、Scaleは(1, 1, 1)に設定します。

親と子に設定したスクリプトのどちらかを無効化し実行します。

親と子両方にスクリプトを設定

↑が実行結果です。

スクリプトを設定した位置で実行結果が変わっていますね、かなり混乱します・・・・。

スクリプトを設定したゲームオブジェクトから見たワールドの位置やローカルの位置になるからです。

InverseTransformDirectionでは親にスクリプトを設定した場合(0.7, 0, 0.7)、子に設定した場合(0, 0, 1)が出力されています。

これは親から見た子の方向をローカルに変更すると子をローカル表示した時にZ軸がそちらの方向を向いているからです。

子に設定した場合は子の向いている方向を子から見ているのでZ軸と子の向いている方向が同じになり(0, 0, 1)になっています。

同じ理由でInverseTransformPointも親から見た子のローカル位置は(1, 1, 1)、子から見た子のローカル位置は(0, 0, 0)となります。

TransformPointも同じで親の場合は(1, 1, 1)、子の場合は(2.4, 2.0, 1.0)となります。

子をドラッグして親子関係を解消すると子のPositionが(2.4, 2.0, 1.0)となるのでわかりやすいと思います。

最後のTransformDirectionの場合は子の値がX軸向きになっています。

TransformDirectionはローカルの方向をワールドに変換するものなので指定している引数がワールドの方向を渡している為おかしな結果となっていると思います。

これらの解説を見てわかるとおり非常にこれらの処理は解り辛いですね・・・・(^_^;)

その為、必要に迫られない限りはこれらの処理は使わない方が混乱の元にならないと思います。

わたくしは完全に混乱しました・・・・・( ノД`)シクシク…

サンプル2

先ほどのサンプルだと少し分かり辛いのでもう一つ別のサンプルを作成してみます。

ワールド空間とローカル空間の変換サンプル2の階層

上のように新しくCubeを作成し、その子要素にCubeを作成します。

Main CameraのPositionをXを1、Yを2、Zを3、RotationのXとZを0、Yを270にします。

親のCubeはPositionのYを2、XZを0、RotationのXYZを0にします。

子のCubeはPositionのXを5、YZを0、RotationのYを180、XZを0とします。

Cubeに以下のスクリプトを取り付けます。

最初のInverseTransformPointは位置をワールド空間からローカル空間へと変換するメソッドで、このスクリプトを取り付けた子要素のCubeのワールド位置をメインカメラの子要素に移動した時のPositionが得られます。

コンソールには

(-3.0, 0.0, -4.0)

が表示されます。Unity実行中に子要素のCubeをドラッグしてMain Cameraの子要素に移動させるとインスペクタのPositionがこの値になります。

InverseTransformDirectionは方向をワールド空間からローカル空間へと変換するメソッドで、ワールドの右方向(Vector3.right)をメインカメラのローカルの方向に変換します。

コンソールには

(0.0, 0.0, -1.0)

が表示されます。

Vector3.rightの方向はメインカメラの反対方向を向いているので上のような値が返ってきます。前方方向は(0.0, 0.0, 1.0)なのでZの値が反転しています。

TransformPointは位置をローカル空間からワールド空間に変換するメソッドで、子要素のCubeのローカル位置をワールド位置に変換しメインカメラの位置に足した結果が得られます。

このサンプルだと分かり辛いですが、メインカメラのローカル空間からこのスクリプトが設定されているゲームオブジェクトの位置分動かした場所の位置が返されます。

メインカメラのRotationのY軸は270にしているので前方はX軸の反対側を向いています。

メインカメラはX軸の反対側を向いている

上のようにメインカメラの前方(青い矢印の方向)はワールド空間のX軸の反対側を向いています。

引数で指定したのはtransform.localPositionでCubeのローカルポジションは(5, 0, 0)となっています。

つまりメインカメラのローカル空間のX軸方向にこのローカルポジションのXを足した位置がTransformPointで返される位置になります。

コンソールには

(1.0, 2.0, 8.0)

が表示されます。

TransformDirectionは方向をローカル空間からワールド空間に変換します。サンプルではメインカメラのVector3.rightの方向を求めています。

コンソールには

(0.0, 0.0, 1.0)

が表示されます。

メインカメラのローカル空間の右側(Vector3.right)をワールド空間にしていて、メインカメラはワールド空間でX軸の反対側を向いていてローカル空間の右側はワールド空間のZ軸方向になっています。

なのでVector3.rightの方向はワールド空間のZ軸方向になるので(0, 0, 1)の値が出力されます。

んー『サンプル2』の項目作りましたがやっぱり混乱しますねぇ・・・・(^_^;)

終わりに

ワールド空間、ローカル空間の値を得るには専用のプロパティを使えば簡単に取得する事が出来ます。

ただし、それらを変換しようと思うと非常に解り辛い処理が絡んできます・・・・。

大体のローカル値の取得はtransform.localPositionやtransform.localRotation、transform.localScaleを使えば事足ります。

変換メソッドは結構混乱しますね・・・・。

コメント

  1. PSP より:

    基本的に、子オブジェクトは、親オブジェクトに追従させたい場合に用いるので、親の位置を原点とした相対座標で良いと思うんです。
    ただ、その子の位置を、他のオブジェクトに参照させたい場合、「親の位置+子の位置」にして、ワールド座標系で指定しておかないと、参照したオブジェクトを基準にした相対座標になってしまいます。
    実際は、回転やスケーリングも入るため、ワールド座標=「親の座標+親の回転(親のスケール・この座標)」になって面倒なので、transform.TransformPoint変換関数や、localToWorldMatrix行列が役に立つんですよね。

タイトルとURLをコピーしました