Unityのtransform.localScaleとlossyScaleの違い|親のScaleは子にどう影響する?
- 1. 今回もWallとBulletHoleで考える
- 2. まずlocalScaleとは?
- 3. Scaleの1とは?
- 4. WallのScaleを2倍にする
- 5. BulletHoleのlocalScaleが1でも大きくなる
- 6. そこでlossyScaleが登場する
- 7. 数字で比較してみる
- 8. 親と子のScaleは掛け合わされる
- 9. positionとの違いも見えてくる
- 10. 図で考えてみる
- 11. localScaleは書き換えられる
- 12. lossyScaleは読み取り専用
- 13. なぜworldScaleというプロパティではないの?
- 14. lossyとは?
- 15. 非一様Scaleとは?
- 16. 親の非一様Scaleと回転が組み合わさると?
- 17. だからlossyScale
- 18. 初心者のうちはどう考えればよい?
- 19. 実際にConsoleへ表示してみる
- 20. 実験例1:親が1倍
- 21. 実験例2:親を2倍
- 22. 実験例3:子を0.5倍
- 23. 弾痕では困ることがある
- 24. なぜ子は親のScaleの影響を受けるの?
- 25. これまでの3つを並べる
- 26. ただしScaleだけ少し違う
- 27. 実験用コード
- 28. Inspectorで見えるScaleは?
- 29. ここは初心者が混乱しやすい
- 30. 親がない場合は?
- 31. Transformの3要素がつながった
- 32. 「local」が付くものは親基準
- 33. まとめ
今回もWallとBulletHoleで考える
これまで、
transform.position
transform.localPosition
では、
position
= 世界から見た位置
localPosition
= 親から見た位置
と学びました。
さらに、
transform.rotation
transform.localRotation
では、
rotation
= 世界から見た向き
localRotation
= 親から見た向き
と学びました。
今回は、Transformの最後の基本要素である、
Scale
を見ていきます。
扱うのは、
transform.localScale
と、
transform.lossyScale
です。
これまでと同じように、
Wall
└ BulletHole
という親子関係を使います。
BulletHoleはWallの子なので、
Wallを移動すれば一緒に移動し、
Wallを回転すれば一緒に回転しました。
では、
Wallの大きさを2倍にしたらどうなるでしょうか?

まずlocalScaleとは?
UnityのTransformには、
transform.localScale
があります。
これは、
親Transformに対する相対的なScale
です。
たとえば、
transform.localScale =
new Vector3(1, 1, 1);
なら、
元の大きさ
です。
transform.localScale =
new Vector3(2, 2, 2);
なら、
親から見て2倍
です。
Scaleの1とは?
TransformのScaleでは、
1
が基本値です。
たとえば、
Scale
X = 1
Y = 1
Z = 1
なら、
元の大きさです。
X = 2
Y = 2
Z = 2
なら、
各方向へ2倍です。
UnityのTransformマニュアルでも、Scaleの値1は元のサイズを表します。
WallのScaleを2倍にする
たとえばWallが、
wall.transform.localScale =
new Vector3(2, 2, 2);
になったとします。
Hierarchyは、
Wall
└ BulletHole
です。
BulletHoleはWallの子なので、
WallのScaleの影響を受けます。
つまり、
Wallが2倍
↓
BulletHoleも一緒に拡大される
ということが起きます。
BulletHoleのlocalScaleが1でも大きくなる
ここが重要です。
BulletHole自身は、
bulletHole.transform.localScale
が、
(1, 1, 1)
だったとします。
それでも親Wallが、
(2, 2, 2)
なら、
BulletHoleは世界から見ると、
おおよそ2倍の大きさになります。
つまり、
BulletHole.localScale
= (1, 1, 1)
だからといって、
世界から見ても1倍とは限らない
ということです。
そこでlossyScaleが登場する
世界側から見たScaleを確認したい場合に使えるのが、
transform.lossyScale
です。
Unity 6では lossyScale は、
オブジェクトのグローバルScaleを表す読み取り専用プロパティ
です。
たとえば、
Debug.Log(
bulletHole.transform.lossyScale
);
とすれば、
世界から見た実効的なScaleを確認できます。
数字で比較してみる
たとえば、
Wall.localScale
= (2, 2, 2)
BulletHole.localScale
= (1, 1, 1)
だったとします。
単純な親子関係なら、
BulletHoleの、
bulletHole.transform.lossyScale
は、
(2, 2, 2)
になります。
つまり、
localScale
= 親から見た倍率
lossyScale
= 世界から見た実効的な倍率
です。
親と子のScaleは掛け合わされる
さらに、
Wall.localScale
= (2, 2, 2)
BulletHole.localScale
= (0.5, 0.5, 0.5)
だったとします。
単純なケースでは、
2 × 0.5
=
1
なので、
BulletHoleの、
transform.lossyScale
は、
(1, 1, 1)
程度になります。
つまり、
親で2倍
子で0.5倍
↓
世界から見ると1倍
です。
positionとの違いも見えてくる
親のScaleを変えると、
子の大きさだけでなく位置にも影響します。
たとえば、
Wall
└ BulletHole
localPosition = (1, 0, 0)
だったとします。
WallのScaleを、
(1, 1, 1)
から、
(2, 2, 2)
へ変更すると、
BulletHoleは親から見た、
localPosition = (1, 0, 0)
を保っていても、
世界から見た距離は広がります。
UnityのTransformマニュアルでも、親のScale変更は子のTransform位置に影響すると説明されています。
図で考えてみる
最初は、
Wall Scale = 1
[Wall]---● BulletHole
↑
localPosition = 1
だとします。
Wallを2倍にすると、
Wall Scale = 2
[ Wall ]------● BulletHole
↑
localPosition = 1
のようになります。
BulletHoleの、
localPosition
は変わっていなくても、
世界での位置は変化します。

localScaleは書き換えられる
localScale は、
設定できます。
たとえば、
bulletHole.transform.localScale =
new Vector3(
0.5f,
0.5f,
0.5f
);
とすれば、
親から見て半分の大きさになります。

lossyScaleは読み取り専用
一方、
transform.lossyScale
は、
読み取り専用
です。
そのため、
transform.lossyScale =
new Vector3(2, 2, 2);
のようには書けません。
lossyScale は、
今、世界から見ると実際にどのくらいのScaleになっているのか?
を確認するために使います。
なぜworldScaleというプロパティではないの?
ここは面白いところです。
位置には、
position
回転には、
rotation
があります。
そのため、
Scaleにも、
scale
のようなワールド用プロパティがありそうに感じます。
しかしUnityでは、
lossyScale
という名前になっています。
lossyとは?
lossy は、
大まかには、
完全な情報を保持できない
という意味です。
なぜこの名前なのでしょう。
Scaleは親子関係が単純なら、
親Scale × 子Scale
で考えやすいです。
しかし、
親に、
非一様Scale
があり、
さらに子や親に、
回転
が加わると、
Transformが歪んだ状態になることがあります。
非一様Scaleとは?
たとえば、
(2, 1, 1)
のように、
Xだけ2倍、
YとZは1倍、
というScaleです。
これを、
非一様Scale
と考えます。
一方、
(2, 2, 2)
なら、
すべて同じ倍率なので、
一様Scaleです。

親の非一様Scaleと回転が組み合わさると?
たとえばWallが、
Scale
(2, 1, 1)
で、
さらに回転しているとします。
その子BulletHoleも別方向へ回転している場合、
世界側では、
単純な、
X倍率
Y倍率
Z倍率
だけでは正確に表しづらい、
歪み
が発生する場合があります。
Unity 6の公式ドキュメントでも、親のScaleと子の回転の組み合わせによってScaleがskewし、3成分のVectorだけでは完全に表現できない場合があると説明されています。
だからlossyScale
そのような場合、
transform.lossyScale
は、
世界での実際のScaleに、
できるだけ近い値
をVector3として返します。
そのため、
完全なworldScale
ではなく、
lossyScale
という名前になっています。
初心者のうちはどう考えればよい?
最初は、
localScale
=
親から見たScale
lossyScale
=
世界から見た実効Scale
と覚えておけば十分です。
そして、
lossyScaleは読み取り専用
も押さえておきましょう。
実際にConsoleへ表示してみる
BulletHole生成後に、
Debug.Log(
"localScale = "
+ bulletHole.transform.localScale
);
Debug.Log(
"lossyScale = "
+ bulletHole.transform.lossyScale
);
を追加します。
実験例1:親が1倍
Wallが、
localScale
(1, 1, 1)
BulletHoleが、
localScale
(1, 1, 1)
なら、
BulletHole.localScale
(1, 1, 1)
BulletHole.lossyScale
(1, 1, 1)
になります。

実験例2:親を2倍
Wallを、
localScale
(2, 2, 2)
にします。
BulletHoleは、
localScale
(1, 1, 1)
のままです。
すると、
BulletHole.localScale
(1, 1, 1)
BulletHole.lossyScale
(2, 2, 2)
となります。
ここを見ると、
違いが一気に分かります。

実験例3:子を0.5倍
さらにBulletHoleを、
localScale
(0.5, 0.5, 0.5)
にします。
親Wallが、
(2, 2, 2)
なら、
単純なケースでは、
BulletHole.lossyScale
(1, 1, 1)
となります。
つまり、
親2倍
×
子0.5倍
=
世界では1倍
です。

弾痕では困ることがある
今回のBulletHoleを考えてみましょう。
Wallを後から、
2倍
にすると、
BulletHoleまで2倍になってしまいます。
しかしゲームによっては、
壁が大きくなっても弾痕のサイズは一定にしたい
こともあります。
この場合、
親のScaleの影響を考える必要があります。
なぜ子は親のScaleの影響を受けるの?
Unityの親子関係は、
Position
Rotation
Scale
を含めたTransformの関係だからです。
つまり、
親が移動
↓
子も移動
親が回転
↓
子も回転
親が拡大
↓
子も拡大
となります。
これで、
Transformの親子関係が一通りつながります。
これまでの3つを並べる
Position
position
= 世界から見た位置
localPosition
= 親から見た位置
Rotation
rotation
= 世界から見た向き
localRotation
= 親から見た向き
Scale
lossyScale
= 世界から見た実効Scale
localScale
= 親から見たScale
です。
ただしScaleだけ少し違う
PositionとRotationは、
transform.position
transform.rotation
のように、
ワールド値を直接設定できます。
しかしScaleでは、
transform.lossyScale
は読み取り専用です。
つまり、
position
↔ localPosition
rotation
↔ localRotation
ほど完全な対称ではありません。
ここはScale特有のポイントです。
実験用コード
これまでのBulletHoleコードに、
Scaleの表示を追加すると次のようになります。
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(
"localScale = "
+ bulletHole.transform.localScale
);
Debug.Log(
"lossyScale = "
+ bulletHole.transform.lossyScale
);
}
}
}
}
この状態で、
親WallのScaleを、
(1, 1, 1)
↓
(2, 2, 2)
↓
(2, 1, 1)
などに変えて、
Consoleの値を比較してみましょう。

Inspectorで見えるScaleは?
Hierarchyが、
Wall
└ BulletHole
となっている場合、
BulletHoleのInspectorに表示されるScaleは、
基本的に、
transform.localScale
に対応する値です。
つまりInspectorで、
Scale
X 1
Y 1
Z 1
と表示されていても、
世界から見たScaleが、
(1, 1, 1)
とは限りません。
親のScaleが影響しているからです。
ここは初心者が混乱しやすい
Inspectorで、
BulletHole
Scale
1, 1, 1
なのに、
画面では2倍に見える。
これはバグではありません。
親Wallが、
Scale
2, 2, 2
だからです。
つまり、
InspectorのScale
=
localScale
と考えると理解しやすくなります。
親がない場合は?
親がないGameObjectなら、
localScale
がそのまま世界でのScaleになります。
たとえば、
Wall
localScale
(2, 2, 2)
で、
親がなければ、
lossyScale
(2, 2, 2)
となります。
Transformの3要素がつながった
ここまでで、
Transformの基本である、
Position
Rotation
Scale
が一通りつながりました。
まとめると、
| 項目 | ワールド側 | 親基準 |
|---|---|---|
| 位置 | position |
localPosition |
| 回転 | rotation |
localRotation |
| Scale | lossyScale ※読み取り専用 |
localScale |
です。

「local」が付くものは親基準
今回までをまとめると、
localPosition
localRotation
localScale
はいずれも、
親を基準にしたTransform値
です。
一方、
position
rotation
lossyScale
は、
世界側から見た状態を確認するために使います。
まとめ
transform.localScale は、
親から見たScale
です。
transform.localScale
は読み書きできます。
一方、
transform.lossyScale
は、
世界から見た実効的なScale
です。
ただし、
読み取り専用
です。
さらに、
親に回転や非一様Scaleがあると、
単純なXYZの3つの倍率では世界側の変形を完全には表せない場合があります。
そのためUnityでは、
worldScale
ではなく、
lossyScale
という名前になっています。
今回までを一言でまとめると、
position
rotation
lossyScale
↓
世界側
localPosition
localRotation
localScale
↓
親側
です。
これで、
Position・Rotation・Scaleと親子関係の基本
が一通りつながりました。
【シリーズ】Raycastで弾痕を作りながら学ぶ(8/9)
← 前の記事:rotationとlocalRotationの違い | シリーズ目次 | 次の記事:Transformとは結局何なのか(総まとめ) →









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