MRが楽しい

MRやVRについて学習したことを書き残す

Unityの回転値Rotationを学びなおす(オイラー角とクォータニオン)

本日は Unity の技術調査枠です。
今回はUnityの回転値である Rotation について説明します。

Rotation 自体はUnity開発で頻繁に利用する概念ですが、そこには一点分かりづらい事があります。
最近またハマったので、きちんとアウトプットすることにしました。

初めに Rotation とは全てのオブジェクトにアタッチされる Transform コンポーネントにある回転値です。
f:id:bluebirdofoz:20170825225359j:plain

例えば、ここで X の値を 90 度に変更すると、オブジェクトは以下のように回転します。
f:id:bluebirdofoz:20170825225408j:plain
X 軸を元に、オブジェクトが 90 度回転しています。直感的で分かりやすいですね。


しかし、本処理をスクリプトで書こうとしたときに、ある問題が発生します。
例えば、以下のようなスペースキーを検知してオブジェクトの Transform 値を取得/設定し、90度ずつ回転を行うスクリプトを作成しました。
・RotationScript.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RotationScript : MonoBehaviour {
    // Update is called once per frame
    void Update()
    {
        // スペースキーを入力したらTransform値を取得し、X軸方向に90度回転する
        if (Input.GetButtonDown("Jump"))
        {
            // Transform値を取得する
            Vector3 position = this.transform.localPosition;
            Quaternion rotation = this.transform.localRotation;
            Vector3 scale = this.transform.localScale;

            // X軸の90度回転?
            rotation.x = rotation.x + 90.0f;

            // Transform値を設定する
            this.transform.localPosition = position;
            this.transform.localRotation = rotation;
            this.transform.localScale = scale;
        }
    }
}

オブジェクトにスクリプトを適用して再生します。
f:id:bluebirdofoz:20170825225506j:plain

スペースキーを押下してみると……。
f:id:bluebirdofoz:20170825225518j:plain
オブジェクトは 90 度回転しませんでした。
178.727 という謎の角度が設定され、オブジェクトがひっくり返っています。


この問題、気付く方はコードを見た時点で気付くと思いますが。
コード上では Rotation の型は Quaternion という型になります。他の変数と違って Vector3 ではありません。
Quaternion 型は x,y,z,w の4軸値で回転を表現する型です。
docs.unity3d.com

しかし、Inspector タブで表示される Rotation は他の Position,Scale と同様に3つの値の設定値となっています。
f:id:bluebirdofoz:20170825225625j:plain
何故か。端的に言うと、Inspector タブで見える Rotation とコード上の rotation は型が全く別です。

Inspector タブで見える Rotation はオイラー角による表現型です。
一方コード上の rotation はクォータ二オンによる表現型なのです。
docs.unity3d.com

クォータ二オンは上記の記事で書かれている通り、直感的に数的表現が理解できません。
よって、この問題を解決するにはどこがクォータ二オンによる表現か理解し、それをオイラー角へ変換する方法を学ぶのが一番です。

変換については以下の二つのメソッドを覚えておけば、問題ありません。
・Quaternion.eulerAngles : クォータニオンオイラー角への変換メソッド
docs.unity3d.com
・Quaternion.Euler : オイラー角 → クォータニオンへの変換メソッド
docs.unity3d.com

よって先ほどのスクリプトを正しく書き直すと以下のようになります。
・RotationScript.cs

sing System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RotationScript : MonoBehaviour {
    // Update is called once per frame
    void Update()
    {
        // スペースキーを入力したらTransform値を取得し、X軸方向に90度回転する
        if (Input.GetButtonDown("Jump"))
        {
            // Transform値を取得する
            Vector3 position = this.transform.localPosition;
            Quaternion rotation = this.transform.localRotation;
            Vector3 scale = this.transform.localScale;

            // クォータニオン → オイラー角への変換
            Vector3 rotationAngles = rotation.eulerAngles;

            // X軸の90度回転
            rotationAngles.x = rotationAngles.x + 90.0f;
            // Vector3の加算は以下のような書き方も可能
            //rotationAngles += new Vector3(90.0f, 0.0f, 0.0f);

            // オイラー角 → クォータニオンへの変換
            rotation = Quaternion.Euler(rotationAngles);

            // Transform値を設定する
            this.transform.localPosition = position;
            this.transform.localRotation = rotation;
            this.transform.localScale = scale;
        }
    }
}

改めて再生してみます。
f:id:bluebirdofoz:20170825225847j:plain

スペースキーを押下してみると……。
f:id:bluebirdofoz:20170825225856j:plain
オブジェクトが期待通り、X 軸方向に 90 度回転しました。成功です。
微妙に 90.00001 となっているのは変換誤差と思われます。

回転値を扱うときは、それがオイラー角かクォータニオンか、意識する事が大切ですね。