とあるクリエイターの技術日記

大学生クリエイターの趣味と技術についての日記

【Unity】スクリプトでRotateをいじると回転が反転するときの対処法

みなさんこんにちは、白黒です。

今回は、Unityで陥りがちな回転のミスについて解説していきます。

原因についても考察しているので、ぜひ最後までご覧ください。

目次

発生した現象

上の映像のように、C#スクリプトでオブジェクトを回転させていくと、急に回転の向きが反転してしまいます

このコードでは、毎フレームオブジェクトの回転を取得して、+5°づつ加えていくようになっています。

(別のコードでフレームレートを固定して、Update関数の実行間隔を調整しています。)

使用したコードは以下の通りです。

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

public class Rotation : MonoBehaviour
{
    // Update is called once per frame
    void Update()
    {
        //オブジェクトの回転をクォータニオンで取得
        Quaternion rotation = this.transform.rotation;

        //クォータニオンからオイラー角に変換
        Vector3 angle = rotation.eulerAngles;

        //X軸基準に+5°回転
        angle.x += 5;

        //オイラー角からクォータニオンに戻してオブジェクトに適用
        this.transform.rotation = Quaternion.Euler(angle);        
    }
}

原因

結論から言うと、原因は取得したクォータニオンオイラー角に変換し、再度クォータニオンに戻していることです。

Unityドキュメントを見てみると、

オイラー角を扱える Quaternion クラス関数を使用してください。 - 回転からオイラー角を取得、変更、再適用することは、意図しない作用の原因になることがあります。

引用元:Unity の回転と向き - Unity マニュアル

とあります。

このような方法だとどうして問題が発生するのでしょうか?

以下は私が個人的に細かい原因を憶測して書いたものなので、
解決法さえわかればいいという方は次の項目まで読み飛ばしてください

まず前提としてUnityは、表示上は回転をx,y,zのオイラー角回転で表していますが、
内部的にはクォータニオンを使用しています。

クォータニオンではオイラー角でいう180°ぶんしか定義されていません。

では、この180°を超える角度はどのように再現しているのでしょうか。

クォータニオンは3つの値を用いて、ひとつの回転軸の方向を決めます。(イメージは空間ベクトルみたいな感じ)

その後に、この軸を中心に先ほどの180°の間で回転を行うのですが、これを越えた時には回転軸の向きを逆にすることで対応しています。

軸まわりの回転は軸に対して右ねじの向きなので、軸が逆向きになった途端に回転の正負が反転してしまうのです。

これにより、軸が逆転する角度を越えたタイミングでプラス向きの回転だったのが、マイナス向きの回転に代わってしまいます。

おそらくはこのようなことが起きていると考えられます。

解決法

解決方法としては、計算をクォータニオンのまま行うというものです。

今回でいうと、オイラー角における+5°をクォータニオンに変換して、
取得したオブジェクトのクォータニオンに対して計算してあげることで回転を実行します。

クォータニオンで角度を足すには、掛け算をしてあげればOKです

最初の方法では、

クォータニオン取得→オイラー角に変換して計算→クォータニオンに変換して適用


という手順なので問題が起きましたが、修正したものは


という手順なので、Unityドキュメントで指摘されている点を回避しています

+5°をクォータニオンに変換するのは、新たな角度を生成しているだけなので問題ありません。

実際に修正したコードは以下のようになります。

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

public class Rotation_edited : MonoBehaviour
{
    // Update is called once per frame
    void Update()
    {
        //オブジェクトの回転をクォータニオンで取得
        Quaternion rotation = this.transform.rotation;

        //X軸基準に+5°の回転を生成し、クォータニオンに変換
        Quaternion rotation5 = Quaternion.Euler(5, 0, 0);

        //クォータニオンで回転を実行してオブジェクトに適用
        this.transform.rotation = rotation * rotation5;
    }
}

これを動かすとこんな感じです。

ちなみに引き算したければ、-5°という角度をクォータニオンに変換すればOKです。

今回だとY軸、Z軸の回転では問題は起きませんでしたが、Unityドキュメントにしたがってクォータニオンのまま計算する方がよいでしょう

おわりに

今回は、Unityを触っているとよくぶつかる回転が反転してしまう問題について話してきました。

この問題は調べてもしっかりと解説されているサイトがあまり無かったので、みなさんの参考になれば幸いです。