Unityでクォータニオン(Quaternion)を扱うための基礎知識その1

クォータニオンって何?

日本語では四元数といいます。

簡単に言うと複素数は平面(二次元)上の点を表すことができるように、クォータニオンは空間(三次元)上の点を表すことができます。

三次元なら要素数が3個あれば表現できるのではないかと思う方もいるかもしれませんが、確かに点を表すだけであれば3個で足ります。

二次元の場合は単純に要素数2個で足りるのですが、実数を2個ではなく虚数という概念を持ち出して複素数を用いるのは意味があるのです。
同様に三次元の場合は、四元数であるクォータニオンを使うことに意味があります。 この辺は数学の分野になるので興味のある方は調べてみてください。

いまいち理解できないのであれば、当面はクォータニオンの概念を応用することで、空間(3次元)で回転操作が簡単にできる。
くらいの認識でも十分です。

UnityではQuaternionというクラスがありますので、Vector3と組み合わせてどのように使えばいいかという方法さえ知っていればプログラムを書く上では問題ないと思います。

クォータニオンと3Dプログラムについて詳しく知りたい方は下記URLが参考になります。

https://www.dropbox.com/s/em7iqkax0771qb0/Unity%E9%81%93%E5%A0%B4%E3%82%B9%E3%83%9A%E3%82%B7%E3%83%A3%E3%83%AB%E5%8D%9A%E5%A4%9A-yasuhara.pdf?dl=0

http://marupeke296.com/DXG_No10_Quaternion.html

クォータニオンは直観的に扱えないので不便と思われがちですが、ジンバルロックの問題が発生しない、計算方法が容易などの利点があるため3Dを扱うゲームでは多くの場合クォータニオンを使用します。

Unityでの位置情報

Transformコンポーネント

f:id:hildsoft:20170609154118p:plain

シーン内のすべてのオブジェクトはTransformコンポーネントを持っています。 コンポーネントはTransformクラスを持ち、位置や回転だけでなく、スケールや親子関係なども管理しています。

ここにあるRotationはオイラー角で表現されていますが、Unityの内部ではクォータニオンで保持されています。

transform.rotationやtransform.localRotationはクォータニオンですので、クォータニオンに慣れるとより理解が深まると思います。

実際の移動メソッド

2017年のUniteTokyoで

transform.SetPositionAndRotation(Vector3, Quaternion);

を使うことが推奨されていました。

このメソッドはUnity5.6から使用できます。

以前使用していた

transform.position = …
transform.rotation = …

こちらは、処理が二回実行されることになるためパフォーマンスが落ちるので
transform.SetPositionAndRotation(Vector3, Quaternion);

を使いましょうとのことです。

positionやrotationの片方だけを設定するなら以前の方法でもいいのかな。

transformクラスの取得方法

スクリプトがアタッチされているオブジェクトは、そのまま

this.transform

でアクセスできます。(this.は省略可)

親オブジェクトの場合は

transform.parent.transform

子オブジェクトの場合は

transform.Find(“ChildObjectName”).gameObject.transform;

全体から検索する場合は

GameObject.Find(“TargetObjectName”).transform;
GameObject.FindWithTag(“TargetTagName”).transform;

スクリプトでpublic定義したオブジェクトの場合は

public GameObject prefabObject;

と定義し、インスペクタで設定後

prefabObject.transform;

で取得できます。(nullチェックは省略)

Findはコストが高いので、初回で参照を取得するなど工夫が必要になる場合もあります。

ベクトルとクォータニオン

ベクトルの取得

Unityではtransformで位置を管理しているので、

transform.position;
transform.localPosition;

などで取得したり、固定の座標などから差分を計算します。

クォータニオンの取得

クォータニオンは計算上便利ですが、直観的ではないため内部の値を直接修正することは殆どありません。

ではどうするかというと、差分や方向などから計算により取得することになります。

クォータニオンは、初期状態からの回転差分と考えた方が分かりやすいかと思います。

transform.SetPositionAndRotation(Vector3, Quaternion);

で第二引数に与えるQuaternionは、初期状態からどの方向にどのくらい回転させるかという情報を保持しています。

毎回初期状態に戻したところから回転させることをイメージしてください。

Vector2とVector3での違い

ここでは空間ベクトルを主に扱いますのでVector3で説明しますが、Vector2はVector3に変換できるのでクォータニオンはVector2のままでも同様に扱うことができます。

Vector2 v2 = Quaternion * Vector2;
Vector3 v3 = Quaternion * Vector3;

よく使うメソッドとプロパティ

上で紹介したスライドでも書いてありましたが、Quaternionでよく使うものは、

  • Quaternion.LookRotation
  • Quaternion.Angle
  • Quaternion.Euler
  • Quaternion.Slerp
  • Quaternion.FromToRotation
  • Quaternion.identity

です。

Quaternion.LookRotation

https://docs.unity3d.com/ja/540/ScriptReference/Quaternion.LookRotation.html

public static Quaternion LookRotation(Vector3 forward, Vector3 upwards = Vector3.up);

向けたい方向に回転させるクォータニオンを取得できます。

Quaternion.Angle

https://docs.unity3d.com/ja/540/ScriptReference/Quaternion.Angle.html

public static float Angle(Quaternion a, Quaternion b);

クォータニオン同士の角度を取得できます。

Quaternion.Euler

https://docs.unity3d.com/ja/540/ScriptReference/Quaternion.Euler.html

public static Quaternion Euler(float x, float y, float z);
public static Quaternion Euler(Vector3 euler);

オイラー角(x,y,z方向それぞれの角度)を指定することで、対応するクォータニオンを取得できます。

Quaternion.Slerp

https://docs.unity3d.com/ja/540/ScriptReference/Quaternion.Slerp.html

public static Quaternion Slerp(Quaternion a, Quaternion b, float t);

aとbの間を任意の割合で回転させたクォータニオンを取得します。

Quaternion.FromToRotation

https://docs.unity3d.com/ja/540/ScriptReference/Quaternion.FromToRotation.html

public static Quaternion FromToRotation(Vector3 fromDirection, Vector3 toDirection);

ベクトル間の角度差を表すクォータニオンを取得します。

Quaternion.identity

https://docs.unity3d.com/ja/540/ScriptReference/Quaternion-identity.html

public static Quaternion identity;

これはメソッドではなく、staticなプロパティです。 無回転を意味します。

初期状態から回転をしない状態なので、回転をリセットする際に使えます。

ここで大事なことは、初期状態とはゲーム開始時ではなく、オブジェクトの回転軸を親またはワールドの軸に合わせることを意味します。

ゲーム開始時の状態に戻したければ、AwakeやStartメソッドが呼ばれる時にTransform.localRotationを保持しておくのが良いでしょう。


サンプルコードまで纏めようとしたらかなり長くなってきたので記事を分割します。

www.hildsoft.com