3D回転で苦労した話

はじめに

こんにちは。コーディング課のみちるです。

2022年春企画で「ShootingStar」という、宇宙船を操作して宇宙空間で自由に飛び回り、敵を倒していく3Dシューティングゲームを制作しました。一応プレイ動画をここに置いておきます。

宇宙船のキャラクター操作を制作する際に、ある問題に苦戦しました。宇宙の広さを体験してもらいたいので、360度自由に飛び回れる動きを作るために、機体の回転動作も自由に操作できなければなりません。そこで、フレームごとに入力を取って、Vector3に格納して、最後にTransformコンポーネントのオイラー角を更新して、回転させようと試みました。

しかし、どうも動作が妙におかしくて、特に縦に90度回転したら左右の回転軸が別の軸になってしまうことが発生します。調べたら、どうやらジンバルロックという問題らしい…

ジンバルロック(Gimbal lock)とはなんぞや!?

まず、ジンバルとは物体の外側にリングが複数はめてある装置のことです。リングの間に物理的に接続されていて、外側のリングを動かすと、中のものも一緒に動きます。つまり、親子関係を持っています。(Unityでの親子順はZXYです)

ジンバル 出典こちら

例えば、上の図はジンバルの構造を示した図です。一番外側にある緑のリングを動かすと、中にある全てのものが一緒に回転します。真ん中にある赤のリングを動かすと、青のリングと物体が共に回転して、緑のリングは回転しません。一番奥にある青のリングを動かすと、中身の物体が回転して、赤と緑のリングは回転しません。

ジンバルロック 出典こちら

そしてジンバルロックとは、特定の角度に回転すると、二つのリングが同じ平面で重なって、一つの方向の回転操作を失ってしまうことです。例えば、もともと3つのリングを動かすとそれぞれ回転する方向が異なるが、上の図の状態では、青いリングと赤いリングが同じ平面で重なって、明らかに青と赤どちらを動かしても、同じ方向に回転してしまいます。平面の視点から見ても、もともと3つのリングがそれぞれ3つの平面上にあって、図の状態では、3つのリングが2つの平面上にしか存在しなくて、1つの平面を失ってしまいました。

解決方法

ジンバルロックは3D空間の回転を扱うときに避けては通れない問題らしく、ネットで調べれば情報が出てきますが、Unityの仕組みも関係してくるので、まずUnityのドキュメントを調べて見ましょう。ドキュメントを調べると、Unityは内部でオイラー角ではなく、クォータニオンで回転の計算を行っています。それは、ジンバルロックを避けることと計算を効率化するためです。クォータニオン(四元数)についてここでは説明しませんが、興味あったらぜひ各自で調べてみてください。(逃げます

注意が必要なのは、Transformコンポーネントで表示されているものはあくまでもクォータニオンをオイラー角に変換した数値にすぎません。さらに、その数値はTransformから取得してきたオイラー角の値とも異なります。

さて、解決方法ですが、ドキュメントでは

オイラー角で値を回転に適用することもできますが、問題を避けるためにクォータニオンとして保存する必要があります。

Unity の回転と向き

と書かれています。では、オイラー角をクォータニオンに変換すれば計算に使えます。そして、クォータニオンの計算では、乗算は回転の合成を意味します。(クォータニオンの計算ややこしいので各自で調べてー) 最後に、フレームごとに入力に対して合成の結果を更新していけば、入力に従って回転できるようになります。

具体的には、入力部分は相変わらずフレームごとにVector3に格納して、Quaternion.EulerでVector3をクォータニオンに変換して掛け合わせるだけです。

これで、宇宙船は宇宙で自由に飛び回ることができました!

まとめ

この記事では、開発中に出会った問題を紹介して、ジンバルロックとその解決方法を説明してみました。ジンバルロックは3D空間を扱うときに注意しなければならない問題で、特に3Dアニメーションでは、キャラクターが簡単なモーションでも予期せぬ動きをする可能性があります。例えばサッカーゲームで、選手の頭が明らかに人間でない動きでボールに突くアニメーションが見えることがあるのはジンバルロックの問題かもしれません。というわけで、ゲームのバグに対して優しい目で見ましょう。

コメントを残す

CAPTCHA