このチュートリアルでは、Unity における Quaternion の Slerp 関数について学びます。この関数を使用すると、指定した時間内で初期の向きから最終の向きへスムーズに回転させることができます。


このチュートリアルの動画版は、現在この言語では利用できません。


動画の書き起こし

こんにちは皆さん。

このチュートリアルでは、Unity における Quaternion の Slerp 関数について学びます。この関数を使用すると、指定した時間内で初期の向きから最終の向きへスムーズに回転させることができます。

このチュートリアルでは、皆さんがすでに Time.deltaTime に慣れていることを前提としています。Time.deltaTime については、以前公開したビデオチュートリアルで解説しました。もし Time.deltaTime に不慣れな場合は、この動画を先に視聴することを強くお勧めします。

このチュートリアルは Unity 2022 を使用して収録しました。ただし、Slerp 関数は複数のバージョンで利用可能であり、今後のリリースでも引き続き使用できる可能性が高いです。

向き A から向き B への回転、より一般的には指定した時間内で値 A から値 B へ補間する処理は、さまざまな方法で行うことができます。値は線形に進行することもあれば、球面補間で進行することもあり、その他にもカスタマイズされた進行方法があります。

動画では、補間の例として、立方体を垂直軸まわりに 90 度回転させる例を紹介しています。シンプルな例ですが、概念を効果的に理解できます。

回転時間は範囲 [0,1] にマッピングされます。時刻 0 では初期値、時刻 1 では最終値になります。範囲内の中間値は、選択した補間方法によって決定される値に対応します。

例では、すべてのキューブが 5 秒で 90 度回転します。しかしアニメーション開始から 4 秒後、これは範囲 [0,1] における 0.8 の瞬間に相当しますが、それぞれのキューブは異なる補間方法を使用しているため、向きが異なります。

回転を行う場合、今回のようなケースでは、線形補間よりも球面補間のほうが好まれます。そのほうが見た目が自然で美しい結果になります。

Unity では、球面補間は Slerp 関数で計算されます。先頭の S は Spherical を意味します。線形補間は Lerp 関数で計算され、先頭の L は Linear を意味します。

先ほど述べたように、Slerp は Quaternion クラスの一部です。Quaternion クラスは、クォータニオンという数学的概念を使用して 3D 空間内の向きや回転を表現します。このチュートリアルではクォータニオンの数学的説明は行いません。

ここでは、クォータニオンは向きを表現するために使用されるということだけを述べ、Slerp 関数の使い方を説明します。

Slerp は向き A から向き B へ遷移するために使用します。

この関数は 3 つのパラメータを取ります。初期の向き、目標の向き、そして範囲 [0,1] 内の評価時刻です。つまり、補間を評価したい瞬間の値です。

Slerp は、指定した瞬間における向きを表すクォータニオンを返します。

したがって、次のように記述すると

Quaternion.Slerp(A, B, 0.2);

これは、向き A から向き B へ移行する回転のうち、5 分の 1 に相当する時点での向きを Unity に取得させることになります。0.2 は 1 の 5 分の 1だからです。

アニメーションを 1 秒間にしたい場合は、Update 内で Time.deltaTime を使用できます。0 から 1 の補間範囲は、Update 内で Time.deltaTime を使用することで、ちょうど 1 秒のアニメーションに変換できます。これは Time.deltaTime のチュートリアルで示した通りです。

一方、任意の秒数でアニメーションを行うには、Time.deltaTime をその秒数で割る必要があります。これについては、後ほど実践例で詳しく説明します。

多くの場合、初期の向きはオブジェクトの向き、つまり transform.rotation です。回転はクォータニオンで表現されるためです。

例えば、初期の向きに角度を度数で加えて目標の向きを計算することは少し難しい場合がありますが、これも実践例ですぐに扱います。では、実践例に進みましょう。

動画では、3D モデルを含むシーンを表示しています。

具体的には、このモデルは 4 つの要素で構成されています。ドアフレーム、2 枚のドアパネル、そしてハンドルです。

ハンドルが付いたドアパネルのピボットポイントは、元のモデルではヒンジの位置にあり、正しく回転できるようになっていました。しかし何らかの理由で、Unity がそれを少し前方に移動してしまいました。

この問題を解決するために、フレームの子として Empty オブジェクトを追加し、それを回転させたいドアパネルのヒンジの位置に配置します。

次に、そのドアパネルを Empty の子にします。

この新しい Empty オブジェクトに回転用スクリプトを追加します。例えば「doorOpening」という名前にします。

このスクリプトでは、ゲーム開始時にドアパネルが自動的に開くようにします。ローカルの垂直軸、この場合は Z 軸まわりに 90 度回転させます。

初期の向きは transform.rotation で取得できます。

利便性のため、またコードを柔軟にするために、Quaternion 型の変数 startRotation を宣言し、Start 内で次のように初期化します。

startRotation = transform.rotation;

最終の向きを表すクォータニオンは、初期の向きにローカル Z 軸まわりの 90 度回転を加えたものになります。では、この回転をクォータニオンでどのように定義するのでしょうか。

Quaternion.Euler 関数を使用します。この関数は XYZ 各軸まわりの回転角度を度数で受け取ります。

まず、実行する回転を表すクォータニオンを定義します。

Quaternion rotationToBePerformed;

そして Start 内で次のように初期化します。

rotationToBePerformed = Quaternion.Euler(0f, 0f, 90f);

このクォータニオンは実行する回転を表しますが、まだ最終の向きではありません。

最終の向きを計算するには、この値を初期の向きに加える必要があります。

ここで重要なポイントがあります。実行する回転を初期の向きに加えるには、2 つのクォータニオンを乗算する必要があります。

そこで、最終のクォータニオンを次のように定義します。

Quaternion targetRotation;

そして Start 内で次のように初期化します。

targetRotation = startRotation * rotationToBePerformed;

次に Update 関数に移動します。Update は各フレームごとに呼び出されます。

次のように記述を開始します。

transform.rotation = Quaternion.Slerp(startRotation, targetRotation

しかしここで止めます。補間を評価するための時間変数をまだ定義していないからです。

この変数は 0 から始める必要があります。Update の外で次のように定義して初期化します。

float evaluation = 0f;

そして、これを Quaternion.Slerp の第 3 引数として使用します。

さらに、この値は各フレームで増加する必要があるため、Update 内で次のように記述します。

evaluation += Time.deltaTime;

回転の最終評価値は 1.0 であることが分かっています。そのため、このアニメーションはちょうど 1 秒間になります。各フレームで evaluation を Time.deltaTime だけ増加させているからです。

ゲームを開始すると、Empty は垂直軸まわりに回転しますが、ドアパネルは開きません。

これは、このオブジェクトを最初に Static に設定していたためです。もともとアニメーションさせる予定がなく、Unity にグローバルイルミネーションを計算させていたからです。しかし今はアニメーションさせたいので、Inspector で Static のチェックを外し、子オブジェクト、つまりハンドルにも適用します。

再度ゲームを開始すると、今度はドアが 1 秒間でスムーズに開くのが確認できます。

では、アニメーションの時間を変更するにはどうすればよいでしょうか。

答えは簡単です。各フレームでの evaluation の増加量を、Time.deltaTime をアニメーション時間で割った値にすればよいのです。

Time.deltaTime だけを使用するとアニメーションは 1 秒になります。例えば 5 秒にしたい場合は、各フレームで Time.deltaTime の 5 分の 1 を加算します。

まず変数を定義します。

float openingDuration = 5f;

アニメーションを 5 秒にしたいからです。そして Update メソッド内の evaluation の更新を次のように変更します。

evaluation += Time.deltaTime / openingDuration;

スクリプトを保存し、Unity エディターに戻ってゲームを開始します。今度はアニメーションが 5 秒で実行されます。

この実践例から、特定の時間内で 2 つの向きの間をスムーズに補間するために Quaternion.Slerp をどのように使用するかが分かります。

このスクリプトは、Unity プロジェクト内のさまざまなシナリオやオブジェクトに応用して、スムーズな回転やアニメーションを実現できます。

まとめます。このチュートリアルでは、実行する回転をクォータニオンで表現する方法、開始クォータニオンに実行する回転を乗算して目標クォータニオンを取得する方法、そして Slerp 関数を使用して球面補間による回転を行う方法を解説しました。Time.deltaTime を使用することで、フレームレートに依存しない一定速度で、可変時間のアニメーションを実現できます。

このチュートリアルがお役に立てば幸いです。それでは、また次回お会いしましょう。

このサイトは、私の制作物の一部を紹介することのみを目的としており、宣伝目的ではありません。現在、カスタム制作、コンサルティング、その他いかなる業務上の協力に関するご依頼も受け付けておらず、対応いたしませんので、あらかじめご了承ください。


プライバシー及びクッキーの使用に関する拡張情報の通知