Unityのtransform.positionとlocalPositionの違い|世界から見た座標と親から見た座標
前回は、弾痕を壁の子GameObjectにしました。
Hierarchyは、
Wall
└ BulletHole
という状態です。
そして、
bulletHole.transform.SetParent(
hit.collider.transform,
true
);
とすることで、
壁が動いたり回転したりしても、弾痕が一緒についてくるようになりました。

ここで新しい疑問が出てきます。
BulletHoleの位置は、どの座標で表されているのでしょうか?
Unityには、位置を表す代表的なプロパティとして、
transform.position
と、
transform.localPosition
があります。
今回は、この2つの違いを見ていきましょう。
- 1. positionは「世界から見た位置」
- 2. localPositionは「親から見た位置」
- 3. 2つを並べてみる
- 4. 「誰から見た座標か」が違う
- 5. 実際にConsoleへ表示してみよう
- 6. 親を動かすとどうなる?
- 7. positionは変わる
- 8. localPositionは変わらない
- 9. これが「子が親についてくる」の正体
- 10. 数字で見るとさらに分かりやすい
- 11. localPositionを0にするとどうなる?
- 12. positionを0にすると意味が違う
- 13. 比較してみよう
- 14. 親がいない場合はどうなる?
- 15. 親を付けると意味が生まれる
- 16. 弾痕ではlocalPositionの考え方がとても自然
- 17. SetParent(true)ともつながる
- 18. 流れを整理すると
- 19. 実験用コード
- 20. Inspectorでも確認できる
- 21. 注意:単純な足し算とは限らない
- 22. 回転する親だとさらに違いが分かる
- 23. transform.positionはいつ使う?
- 24. transform.localPositionはいつ使う?
- 25. まとめ
positionは「世界から見た位置」
まず、
transform.position
です。
これは、
Unityの世界全体から見たGameObjectの位置
を表します。
Unity 6の公式リファレンスでも、Transform.position は「Transformのワールド空間上の位置」とされています。
たとえばWallが、
X = 10
Y = 0
Z = 0
にあるとします。
そしてBulletHoleが、
X = 10.5
Y = 1
Z = 0
にあるとします。
この場合、
bulletHole.transform.position
は、
(10.5, 1, 0)
です。
これは、
シーン全体の原点から見てどこにいるか
を表しています。
localPositionは「親から見た位置」
一方、
transform.localPosition
は、
親Transformから見た相対位置
です。
Unity 6の公式リファレンスでも、localPosition は親Transformに対する相対位置と説明されています。
先ほど、
Wall
└ BulletHole
という親子関係がありました。
Wallが、
World Position
(10, 0, 0)
にあり、
BulletHoleが、
World Position
(10.5, 1, 0)
にあるとします。
WallからBulletHoleを見ると、
右へ 0.5
上へ 1
です。
したがって、単純な回転・スケールのない例なら、
bulletHole.transform.localPosition
は、
(0.5, 1, 0)
になります。
2つを並べてみる
同じBulletHoleでも、
transform.position
と、
transform.localPosition
では値が違います。
たとえば、
Wall
position
(10, 0, 0)
└ BulletHole
position
(10.5, 1, 0)
localPosition
(0.5, 1, 0)
です。
つまり、
position
↓
世界全体から見た場所
localPosition
↓
親から見た場所
です。

「誰から見た座標か」が違う
ここが今回の最重要ポイントです。
どちらも、
位置
を表していることに変わりありません。
違うのは、
どこを基準にしているか
です。
transform.position
なら、
Unity世界の原点
(0, 0, 0)
を基準にします。
一方、
transform.localPosition
なら、
親GameObject
を基準にします。
一言で言えば、
position
=
世界から見た座標
localPosition
=
親から見た座標
です。

実際にConsoleへ表示してみよう
BulletHoleを生成したあとに、
Debug.Log(
"position = "
+ bulletHole.transform.position
);
Debug.Log(
"localPosition = "
+ bulletHole.transform.localPosition
);
を追加してみましょう。
たとえば、
position = (10.50, 1.00, 0.00)
localPosition = (0.50, 1.00, 0.00)
のような違いを確認できます。
親を動かすとどうなる?
ここからが面白いところです。
現在、
Wall
position = (10, 0, 0)
└ BulletHole
position = (10.5, 1, 0)
localPosition = (0.5, 1, 0)
だとします。
Wallを、
X = 10
から、
X = 20
へ移動してみます。
するとBulletHoleも親について移動します。
結果は、
Wall
position = (20, 0, 0)
└ BulletHole
position = (20.5, 1, 0)
localPosition = (0.5, 1, 0)
となります。
ここに重要な違いがあります。

positionは変わる
Wallが移動すると、
BulletHoleの、
transform.position
も変わります。
なぜなら、
BulletHoleは世界全体から見ると、
実際に別の場所へ移動している
からです。
移動前
position = (10.5, 1, 0)
移動後
position = (20.5, 1, 0)
となります。
localPositionは変わらない
一方、
transform.localPosition
は、
(0.5, 1, 0)
のままです。
なぜでしょう。
BulletHoleから見れば、
Wallから右へ0.5
Wallから上へ1
という関係は変わっていないからです。
つまり、
Wall
● BulletHole
親から見た位置関係
は、そのままなのです。
これが「子が親についてくる」の正体
前回、
親を動かすと子もついてくる
と学びました。
今回の内容とつなげると、
その仕組みがさらに分かりやすくなります。
BulletHoleは、
localPosition
によって、
Wallから見てこの位置にいる
という関係を持っています。
Wall自体が動くと、
その相対関係を保ったまま、
position
が変化します。
つまり、
localPosition
↓
親との位置関係を保持
親が移動
↓
世界での位置が変わる
position
↓
新しいワールド座標になる
ということです。
数字で見るとさらに分かりやすい
最初は、
Wall.position
= 10
BulletHole.localPosition
= 0.5
です。
単純化すれば、
BulletHole.position
10 + 0.5
=
10.5
と考えることができます。
次にWallを、
20
へ動かします。
すると、
20 + 0.5
=
20.5
となります。
BulletHoleの、
localPosition = 0.5
は変わっていません。
変わったのは親の位置です。
localPositionを0にするとどうなる?
では、
bulletHole.transform.localPosition =
Vector3.zero;
としてみましょう。
Vector3.zero は、
(0, 0, 0)
です。
これは、
親から見て位置0
という意味です。
つまりBulletHoleは、
親Wallの原点と同じ場所
へ移動します。
positionを0にすると意味が違う
一方、
bulletHole.transform.position =
Vector3.zero;
とすると、
BulletHoleは、
世界の原点
(0, 0, 0)
へ移動します。
同じ、
Vector3.zero
を代入しているのに、結果が大きく違います。
比較してみよう
transform.position =
Vector3.zero;
なら、
世界の原点へ移動
です。
一方、
transform.localPosition =
Vector3.zero;
なら、
親の原点へ移動
です。
これは非常に重要な違いです。
親がいない場合はどうなる?
では、GameObjectに親がない場合はどうでしょうか。
たとえば、
Hierarchy
Player
だけで、
Playerに親がない場合です。
この場合、
transform.position
と、
transform.localPosition
は同じ値になります。
Unity 6の公式ドキュメントにも、親がない場合は localPosition と position は同じになると記載されています。
つまり、
親なし
position
=
localPosition
です。
親を付けると意味が生まれる
localPosition が特に重要になるのは、
Parent
└ Child
という親子関係がある場合です。
たとえば、
Car
└ Camera
なら、
Cameraの localPosition は、
車から見てカメラがどこにあるか
を表します。
Player
└ Weapon
なら、
Weaponの localPosition は、
Playerから見て武器がどこにあるか
を表します。
今回なら、
Wall
└ BulletHole
なので、
BulletHoleの localPosition は、
Wallから見て弾痕がどこに付いているか
を表します。
弾痕ではlocalPositionの考え方がとても自然
弾痕について考えてみると、
世界のどこにあるかより、
壁のどこに付いているか
の方が重要です。
つまり、
BulletHole.position
よりも、
BulletHole.localPosition
の考え方が、弾痕と壁の関係を表すうえで自然です。
もちろん実際のRaycastでは、
hit.point
というワールド座標を使って最初の配置を行っています。
そのあとWallの子にすることで、
ワールド上の当たった位置
↓
親から見た相対位置
という関係が作られます。
SetParent(true)ともつながる
前回は、
bulletHole.transform.SetParent(
hit.collider.transform,
true
);
としました。
この true は、
現在のワールド位置を保ったまま親を変更する
という指定でした。
つまり最初に、
hit.point
を使って、
世界の正しい位置にBulletHoleを作ります。
そして、
SetParent(..., true)
によって、
見た目の位置を変えずにWallの子にします。
するとUnity側で、
その位置に対応する、
localPosition
が計算されます。

流れを整理すると
hit.point
↓
ワールド座標を取得
Instantiate
↓
ワールド座標へ弾痕を置く
SetParent(..., true)
↓
見た目の場所を保ったまま
Wallの子にする
localPosition
↓
Wallから見た相対位置が決まる
という流れです。
これまで学んだ内容がここでつながります。
実験用コード
次のようにすると、
position と localPosition を同時に確認できます。
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(
"position = "
+ bulletHole.transform.position
);
Debug.Log(
"localPosition = "
+ bulletHole.transform.localPosition
);
}
}
}
}
弾痕を作ったあとに親Wallを動かして、再び値を確認してみると理解しやすいでしょう。
Inspectorでも確認できる
この違いはコードだけでなく、Inspectorでも確認できます。
たとえばHierarchyが、
Wall
└ BulletHole
なら、
BulletHoleを選択したときのTransformのPosition欄は、
基本的に、
親から見たローカル位置
として表示されます。
そのため、
コードの、
transform.localPosition
と対応させて見ると理解しやすくなります。
注意:単純な足し算とは限らない
先ほど、
親のposition
+
localPosition
=
子のposition
のように説明しました。
これは、
親に回転やScaleがない単純な例
では理解しやすい考え方です。
しかし実際には、親の、
Rotation
Scale
も子のワールド位置に影響します。
Unity公式ドキュメントでも、親Transformの回転やScaleが localPosition からワールド位置を計算するときに適用されると説明されています。
そのため、常に単純な、
position + localPosition
だけで求まるわけではありません。
初心者のうちは、
position
=
世界基準
localPosition
=
親基準
をまず理解しておけば十分です。
回転する親だとさらに違いが分かる
Wallを90度回転させてみると、
BulletHoleも一緒に回ります。
このとき、
bulletHole.transform.localPosition
は、
Wallを基準にした位置なので、
基本的な親子の位置関係を維持します。
しかし、
bulletHole.transform.position
は、
世界全体から見た位置なので変化します。
これによって、
localとは「自分または親を基準にした世界」
という感覚が見えてきます。

transform.positionはいつ使う?
たとえば、
敵が世界のどこにいるか知りたい
プレイヤーを特定の地点へ移動したい
Raycastのhit.pointへ移動したい
など、
シーン全体を基準に位置を扱いたい場合
には、
transform.position
が分かりやすいでしょう。
transform.localPositionはいつ使う?
一方、
武器を手の少し前へ置く
カメラを車体の後ろへ置く
UIやパーツを親基準で配置する
弾痕を壁の特定位置に保持する
など、
親との位置関係を扱いたい場合
には、
transform.localPosition
が重要になります。
まとめ
今回の違いは、とてもシンプルです。
transform.position
は、
世界から見た位置
です。
transform.localPosition
は、
親から見た位置
です。
たとえば、
Wall.position
= (10, 0, 0)
BulletHole.position
= (10.5, 1, 0)
BulletHole.localPosition
= (0.5, 1, 0)
という関係になります。
そしてWallを、
(20, 0, 0)
へ動かすと、
BulletHole.position
= (20.5, 1, 0)
BulletHole.localPosition
= (0.5, 1, 0)
となります。
つまり、
親が動く
↓
position
世界から見た場所なので変わる
localPosition
親から見た場所なので変わらない
ということです。
今回の Wall → BulletHole を通して考えると、
position と localPosition は別の位置を表しているのではなく、同じGameObjectの位置を「どこを基準に見るか」が違う
ということが分かります。

【シリーズ】Raycastで弾痕を作りながら学ぶ(6/9)
← 前の記事:弾痕を壁の子にする(SetParent) | シリーズ目次 | 次の記事:rotationとlocalRotationの違い →









ディスカッション
コメント一覧
まだ、コメントがありません