Unityのtransform.rotationとlocalRotationの違い|世界から見た向きと親から見た向き

広告

今回もWallとBulletHoleで考える

前回は、

transform.position
transform.localPosition

の違いを学びました。

ポイントは、

position
= 世界から見た位置

localPosition
= 親から見た位置

でした。

今回は、その「回転版」です。

扱うのは、

transform.rotation

と、

transform.localRotation

です。

前回と同じように、

Wall
└ BulletHole

という親子関係があるとします。

BulletHoleは、

bulletHole.transform.SetParent(
    hit.collider.transform,
    true
);

によってWallの子になっています。

そのため、Wallが回転するとBulletHoleも一緒に回ります。

ここで、

BulletHole自身の回転はどう表されているのか?

を考えてみましょう。

壁をクリックして弾痕を生成し、壁の子にするまでの流れ

transform.rotationとは?

まず、

transform.rotation

です。

これは、

世界全体から見たGameObjectの向き

を表します。

つまり、

ワールド座標系から見て
どちらを向いているか

という情報です。

transform.localRotationとは?

一方、

transform.localRotation

は、

親Transformから見た相対的な向き

です。

今回なら、

Wallから見て
BulletHoleがどちらを向いているか

を表します。

つまり、

rotation
= 世界から見た向き

localRotation
= 親から見た向き

です。

まず単純な状態で考える

たとえばWallが回転していないとします。

Wall
Rotation = 0°

└ BulletHole

BulletHoleもWallと同じ方向を向いているなら、

BulletHole.rotation
= 0°

BulletHole.localRotation
= 0°

と考えられます。

この段階では、

rotation
localRotation

の違いが分かりにくいかもしれません。

そこでWallを回転させてみます。

壁も弾痕も回転していない初期状態では、rotationもlocalRotationも(0, 0, 0)

Wallを90度回転させる

WallをY軸方向へ90度回転したとします。

Wall
Rotation Y = 90°

BulletHoleはWallの子なので、

一緒に90度回転します。

世界から見るとBulletHoleも、

Y = 90°

の方向を向きます。

そのため、

bulletHole.transform.rotation

は、世界から見て90度回転した状態になります。

壁をY軸に90度回転させると、rotationは(0, 90, 0)になりlocalRotationは(0, 0, 0)のまま

でもlocalRotationは変わらない

ここが重要です。

WallとBulletHoleの向きの関係が、

Wallの面に対して
BulletHoleは正面を向いている

ままなら、

bulletHole.transform.localRotation

は変わりません。

つまり、

Wallを回転
↓
BulletHoleも一緒に回転
↓
世界から見た向きは変わる
↓
親から見た向きは同じ

となります。

positionとの関係と同じ

前回は、

親が移動する
↓
positionは変わる
localPositionは変わらない

でした。

今回は、

親が回転する
↓
rotationは変わる
localRotationは変わらない

です。

考え方はほとんど同じです。

図で考えてみる

最初は、

世界

↑

Wall
└ BulletHole

で、どちらも正面を向いているとします。

その後、Wallを右へ90度回転します。

世界

        →
Wall
└ BulletHole

BulletHoleも一緒に右を向きます。

世界から見れば、

向きが変わった

ので rotation は変わります。

しかしWallから見れば、

BulletHoleはWallに対して
同じ向きのまま

なので localRotation は変わりません。

壁をY軸に90度回転する前と後を、横から見たイメージ

実際にConsoleへ表示してみよう

BulletHoleを生成したあと、

Debug.Log(
    "rotation = "
    + bulletHole.transform.rotation.eulerAngles
);

Debug.Log(
    "localRotation = "
    + bulletHole.transform.localRotation.eulerAngles
);

としてみましょう。

Quaternion のままだと初心者には見づらいので、

.eulerAngles

を付けて、

X
Y
Z

の角度として表示すると分かりやすくなります。

たとえば最初は

Wallが回転していない状態なら、

rotation
(0, 0, 0)

localRotation
(0, 0, 0)

のようになるかもしれません。

Wallを90度回転したあと

WallをY軸に90度回転すると、

BulletHoleの、

rotation

は、

(0, 90, 0)

のように変わります。

一方、

localRotation

は、

(0, 0, 0)

のままになることがあります。

つまり、

世界から見ると90度回った

親から見ると向きは変わっていない

ということです。

HierarchyとInspectorで、Wallと弾痕のRotationを確認する

localRotationを0にするとどうなる?

では、

bulletHole.transform.localRotation =
    Quaternion.identity;

としてみましょう。

Quaternion.identity は、

回転なし

を表します。

ただし、

世界から見て回転なし

ではありません。

ここが大切です。

localRotation に対して設定しているので、

親から見て回転なし

という意味になります。

つまりBulletHoleは、

親Wallと同じ向き

になります。

localRotationを(0, 0, 45)に変更すると、壁から見て45度回転する

rotationをidentityにすると意味が違う

一方、

bulletHole.transform.rotation =
    Quaternion.identity;

とすると、

BulletHoleは、

世界から見て回転なし

になります。

つまり、

世界の基準方向

へ向きます。

Wallが90度回転していたとしても、

BulletHoleだけ世界基準の方向へ戻ることになります。

rotationを(0, 90, 0)に変更すると、世界基準で90度回転する

同じidentityでも意味が違う

transform.rotation =
    Quaternion.identity;

は、

世界基準で回転なし

です。

一方、

transform.localRotation =
    Quaternion.identity;

は、

親基準で回転なし

です。

前回の、

position = Vector3.zero
localPosition = Vector3.zero

と同じ考え方です。

rotationとlocalRotationを整理する

プロパティ 基準
transform.rotation 世界全体
transform.localRotation 親Transform

一言で言えば、

rotation
→ 世界から見た向き

localRotation
→ 親から見た向き

です。

transform.rotationとtransform.localRotationの違いのまとめ

BulletHoleではどちらが重要?

今回のBulletHoleは、

Wall
└ BulletHole

という関係です。

弾痕にとって重要なのは、

世界のどちらを向いているか

よりも、

Wallに対してどちらを向いているか

です。

なぜならWallが回転しても、

弾痕はWallに貼り付いた状態を保つ必要があるからです。

その意味では、

localRotation

の考え方が非常に重要です。

最初の向きはhit.normalから作っていた

ここで前の記事ともつながります。

弾痕の生成時には、

Quaternion rotation =
    Quaternion.LookRotation(
        hit.normal
    );

としていました。

これは、

当たった面の方向
↓
世界空間上の回転

を作っています。

そのあと、

Instantiate(
    bulletHolePrefab,
    position,
    rotation
);

で、まず正しいワールド回転で弾痕を生成しています。

そのあと親にしていた

次に、

bulletHole.transform.SetParent(
    hit.collider.transform,
    true
);

としていました。

true なので、

現在のワールド位置・回転を保つ

ように親が変更されます。

つまり、

まず世界基準で正しい向きにする
↓
その見た目を保ったまま
Wallの子にする
↓
localRotationが自動的に決まる

という流れです。

ここが非常に重要

生成時は、

rotation

というワールド回転を使っています。

親にしたあとでは、

localRotation

として、

Wallとの相対的な向きが保持されます。

つまり、

生成時
→ 世界基準

親子関係を作った後
→ 親基準の関係も持つ

ということです。

実験コード

次のようにすると、

rotation

と、

localRotation

を比較できます。

using UnityEngine;
using UnityEngine.InputSystem;

public class BulletHoleShooter : MonoBehaviour
{
    [SerializeField]
    GameObject bulletHolePrefab;

    void Update()
    {
        if (Mouse.current.leftButton.wasPressedThisFrame)
        {
            Ray ray =
                Camera.main.ScreenPointToRay(
                    Mouse.current.position.value
                );

            RaycastHit hit;

            if (Physics.Raycast(ray, out hit))
            {
                Vector3 position =
                    hit.point
                    + hit.normal * 0.01f;

                Quaternion rotation =
                    Quaternion.LookRotation(
                        hit.normal
                    );

                GameObject bulletHole =
                    Instantiate(
                        bulletHolePrefab,
                        position,
                        rotation
                    );

                bulletHole.transform.SetParent(
                    hit.collider.transform,
                    true
                );

                Debug.Log(
                    "rotation = "
                    + bulletHole.transform
                        .rotation.eulerAngles
                );

                Debug.Log(
                    "localRotation = "
                    + bulletHole.transform
                        .localRotation.eulerAngles
                );
            }
        }
    }
}

Wallを回転させながら値を見ると、

違いが分かりやすくなります。

親を回転させて確認する

たとえば、

Wallの回転を、

Y = 0°

から、

Y = 90°

へ変更してみます。

すると、

BulletHoleの、

rotation

は変化します。

しかしWallとの相対的な向きが変わっていなければ、

localRotation

は基本的に同じままです。

これが、

子オブジェクトが親の回転についていく仕組み

です。

localRotationはQuaternion

注意点として、

transform.localRotation

の型は、

Quaternion

です。

transform.rotation

も同じく、

Quaternion

です。

つまり、

rotation
localRotation

の違いは、

型の違いではありません。

どちらもQuaternionです。

違うのは、

基準となる座標系

です。

Euler角で考えすぎない

Inspectorでは、

X
Y
Z

の角度が見えるため、

回転を、

0°
90°
180°

のように考えがちです。

しかしUnity内部では、

Quaternion

として回転を扱っています。

今回、

.eulerAngles

を使うのは、

確認しやすく表示するため

です。

回転そのものはQuaternionとして扱われている、

という点は覚えておきましょう。

親がない場合は?

GameObjectに親がない場合、

親基準
=
世界基準

になります。

そのため、

transform.rotation

と、

transform.localRotation

は同じ回転を表します。

これは、

position
localPosition

の場合と同じです。

親子関係があると違いが重要になる

たとえば、

Player
└ Weapon

なら、

weapon.transform.localRotation

は、

Playerから見てWeaponがどちらを向いているか

を表します。

Car
└ Camera

なら、

Carから見てCameraがどちらを向いているか

です。

今回なら、

Wall
└ BulletHole

なので、

Wallから見てBulletHoleがどちらを向いているか

になります。

rotationを使う場面

たとえば、

世界の北方向へ向ける

特定のワールド方向へ向ける

hit.normalの方向へ向ける

など、

世界基準の向き

を扱いたい場合には、

transform.rotation

が分かりやすいでしょう。

localRotationを使う場面

一方、

武器をPlayerに対して少し傾ける

CameraをCarに対して向ける

子パーツを親から見て回転させる

など、

親との向きの関係

を扱いたい場合には、

transform.localRotation

が重要になります。

positionとの対応で覚える

4つをまとめると、非常に分かりやすくなります。

プロパティ 意味
position 世界から見た位置
localPosition 親から見た位置
rotation 世界から見た向き
localRotation 親から見た向き

つまり、

position
rotation
↓
ワールド基準

localPosition
localRotation
↓
親基準

です。

「local」が付いたら親基準

初心者のうちは、

local

という言葉に注目すると覚えやすいでしょう。

localPosition

なら、

親から見た位置

localRotation

なら、

親から見た回転

です。

逆に、

position
rotation

は、

世界基準

と考えると整理しやすくなります。

今回の流れを整理する

これまでの記事をつなげると、

Raycast
↓
hit.point
↓
ワールド位置を取得

hit.normal
↓
ワールド方向を取得

Instantiate
↓
ワールド位置・回転で弾痕を作る

SetParent
↓
Wallの子にする

localPosition
↓
Wallから見た位置

localRotation
↓
Wallから見た向き

となります。

最初はRaycastの話でしたが、

ここまで進むと、

UnityのTransformと座標系の考え方

まで自然につながってきます。

まとめ

transform.rotation

は、

世界全体から見たGameObjectの回転

です。

transform.localRotation

は、

親Transformから見た相対的な回転

です。

親Wallを回転させると、

BulletHole.rotation
↓
世界から見た向きなので変わる

BulletHole.localRotation
↓
親との向きが同じなら変わらない

となります。

前回の位置と並べて覚えると、

position
= 世界から見た位置

localPosition
= 親から見た位置

rotation
= 世界から見た向き

localRotation
= 親から見た向き

です。

つまり、

local が付いていないものはワールド基準、local が付いているものは親基準

と整理すると、UnityのTransformがかなり理解しやすくなります。


【シリーズ】Raycastで弾痕を作りながら学ぶ(7/9)
← 前の記事:positionとlocalPositionの違い | シリーズ目次 | 次の記事:localScaleとlossyScaleの違い →

訪問数 3 回, 今日の訪問数 3回

広告

Unity

Posted by hidepon