本日は Unity の技術調査枠です。
Unityのジンバルロックの動きを理解して制御してみたので記事にしてみます。
ジンバルロック
複数の軸の回転を組み合わせたとき、1つの回転軸の自由度が失われてしまう事象のことです。Unity のトランスフォームの回転では X 軸の回転に 90 度または -90 度を設定したときに発生します。
発生事例
以下の Plane オブジェクトの Rotation を変化させてジンバルロックを発生させてみます。
・X:0, Y:0, Z:0
Rotation に X:90, Y:0, Z:0 を設定します。
・X:90, Y:0, Z:0
この後、Y 軸と Z 軸の値をそれぞれ変更してみます。
すると異なる回転軸を設定しているはずが、回転方向が同じになってしまいます。
この1つの回転軸の自由度が失われてしまう事象がジンバルロックです。
・X:90, Y:90, Z:0
・X:90, Y:0, Z:-90
ジンバルロックの動き
ジンバルロックを視覚的に理解するには以下の動画が分かりやすいです。
赤のリングを 90 度の回転で固定したとき、青のリングと緑のリングの回転方向が同じになります。
youtu.be
Unityでの発生の仕組み
オブジェクトのローカル座標で回転軸を考えた時、Unityでは、Y軸 -> X軸 -> Z軸 の順に回転が処理されます。
(ワールド座標で回転軸を捉えた場合は Z軸 -> X軸 -> Y軸 の順になります)
先ほどの動画では赤のリングがX軸にあたり、このため、X軸を 90 度または -90 度に設定したときジンバルロックが発生します。
発生事例では以下の順で処理が行われていたことになります。
X:90, Y:90, Z:0の例
初めにオブジェクトのローカル座標のY軸方向に 90 度回転する計算が行われる。
次にオブジェクトのローカル座標のX軸方向に 90 度回転してこの方向になる。
X:90, Y:0, Z:-90の例
初めにオブジェクトのローカル座標のX軸方向に 90 度回転する計算が行われる。
その次にオブジェクトのローカル座標のZ軸方向に -90 度回転してこの方向になる。
ジンバルロックを回避する
上記の通り、内部の処理では回転の順番が Y軸 -> X軸 -> Z軸 で行われてしまうことがジンバルロックが発生する原因です。
つまり、回転の順番を制御すればジンバルロックにより回転軸の自由度が失われてしまう事象を回避して意図通りの回転が行えます。
サンプルスクリプト
以下の2種類の回転を行うサンプルスクリプトを作成しました。
ChangeSameTime 関数は X:90 度と Y:90 度を同時に設定してオブジェクトを回転させます。
ChangeOrder 関数は順に X:90 度を設定して次に Y:90 度を設定してオブジェクトを回転させます。
・RotationTest.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; public class RotationTest : MonoBehaviour { [ContextMenu("ChangeSame")] public void ChangeSameTime() { this.transform.localRotation = Quaternion.Euler(90, 90, 0); } [ContextMenu("ChangeOrder")] public void ChangeOrder() { this.transform.localRotation = Quaternion.Euler(90, 0, 0); this.transform.localRotation *= Quaternion.Euler(0, 90, 0); } }
動作確認
シーンを再生して動作を確認します。
ChangeSameTime 関数を実行すると、ジンバルロックが発生したときと同じ方向になります。
また、エディターでの表記上は X:90, Y:0, Z:-90 と表示されます。
方向をリセットするため、シーンを一旦停止して再び開始します。
ChangeOrder 関数を実行すると、ジンバルロックを回避して異なる方向を向けることができました。
エディターでの表記上は X:0, Y:90, Z:90 と表示されます。