【Unity】Non-Uniform Scalingな親GameObjectを持つと子は苦労するかもしれない

# Non-Uniform Scalingとは
X, Y, Z軸のスケール値が同一でないものを指します。
日本語では「不均等スケール」というような表現をします。

反対に、X, Y, Z軸のスケール値が全て同じであるものを、Uniform Scaling/均等スケールと言います。

# Non-Uniform Scalingで起こる問題
実験用のシーンを作りました。
クリアキューブの中にUnityちゃんが入っており、親子関係になるようにしています。
初期状態では全てScale1に設定しています。
![UnityChanInCube初期状態.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1605307/d1125ff5-0a92-4fec-2780-a07c08485441.png)

### Non-Uniform Scaling状態生成
キューブのScaleをY軸方向2倍に変更します。
![親のスケールをY軸2倍に.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1605307/5fafa912-7059-7916-d97c-65825be7bec4.png)

子階層にあるUnityちゃん含め縦長になりました。
これでキューブがNon-Uniform Scalingの状態となりました。

### 子GameObjectを回転
この状態で、UnityちゃんをZ軸で-90度回転してみます。
![UnityChanをZ軸-90回転すると….png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1605307/231cbd6c-9bcf-8667-3a0c-e7b271a92fec.png)

…あれ、Unityちゃんなんか太った…？

見比べるために、UnityちゃんのRotationを元に戻し、キューブの方をZ軸で-90度回転してみます。
![親をZ軸-90度回転した場合.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1605307/d8696e09-6bd2-6e31-6c6f-98144e5b22e1.png)
うん、身長2倍状態。
UnityちゃんのRotationを変更した場合もこの見た目になることを期待していたんだけど…。

### Unityちゃんの方のRotationを変更して身長2倍を実現するには
Scaleを以下のように設定すれば同じ見た目になります。
![親を回転した場合と同じになるようにUnityChanのスケール調整.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1605307/2eb85967-904c-9041-e74c-bf85c4b35246.png)

回転角度が90の倍数の場合はこのように帳尻を合わせることができますが、例えば45度回転では変形したUnityちゃんを元に戻すことはできません。[^1]
[^1]: シェーダーを使えば何とかできるとは思いますが、シェーダーなら元に戻すより回転自体をシェーダーで行った方が良いですよね。


# 何が起こっているのか
2D画面で改めて状況を確認してみます。
今度はクリアキューブの中に緑色のキューブを入れました。
緑キューブはScale0.5に設定しています。
![CubeInCube初期状態.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1605307/c3dfb405-c369-a698-a958-1d6df3f715ca.png)

### 子GameObject回転
緑キューブをZ軸で90度回転しました。
![子をZ軸90度回転.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1605307/09b182e1-6a64-9f33-5d38-74fd5e102150.png)
回転したのに見た目が同じです。
ローカル軸の向きは変わっているので、確かに回転はされています。

次は、緑キューブをZ軸で45度回転しました。
![子をZ軸45度回転すると….png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1605307/5a7e1f7a-c0ce-2669-1ebc-6fa90f22fe11.png)
すると、緑キューブが縦長の菱形に変形しました。

緑キューブは回転しても常に縦方向に長いです。
つまり、子GameObjectの向きに関わらず、親GameObjectの軸でスケーリングされてしまっています。

### 子GameObjectのScale変更はどうなる？
因みに、緑キューブをZ軸で90度回転した状態で、緑キューブのScaleをY軸方向に2倍にすると、横に伸びます。
![子のスケールをY軸2倍に.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1605307/de828e61-a9bd-4f04-769b-29bd809d7a88.png)
ということは、子GameObjectのLocalScaleは子GameObjectの軸でスケールされているようです。

## サポート外とのこと
[UnityのTransformのマニュアル](https://docs.unity3d.com/2022.1/Documentation/Manual/class-Transform.html)に説明がありました。
（日本語版は少し訳がわかりにくいので英語の方を引用します）
> For performance reasons, a child object of a non-uniformly scaled parent will not have its scale automatically updated when it rotates. 

子GameObjectが回転しても、スケール方向は回転前の状態の軸（つまり親GameObjectの軸）となるということのようです。

このため、Non-Uniform Scalingな親を持つ子GameObjectは任意の方向に拡大縮小させることができません。
上記のマニュアルによると、その他にもサポート外となるものが複数あります。
また、今回の実験シーンでは確認できませんでしたが、親をNon-Uniform Scalingにしただけでフレームレートがガタ落ちしたという情報も見かけました。


# 対応策
サポート外なのでどうしようもない、というところではあるのですが、ヒエラルキーの構造を工夫することで避けられる場面も多いと思います。
- スケール変更が不要なモデルを準備する。
- Non-Uniform ScalingにしたGameObjectには子階層を作らない。
- 2Dの場合はX, Y軸のスケール値を変更したらZ軸にも同じ値を入れるようにする。

ということを気にしながらヒエラルキーを構成していくとちょっぴり幸せになれるかもしれません。