【Unity】回転するオブジェクトの角速度と回転軸を計算する

こじゃらこじゃら

回転しているオブジェクトの回転速度を知りたい場合はどうすればいいの?

このはこのは

オブジェクトの回転量から計算できるわ。回転量を求めるにはクォータニオンを用いるの。

回転しているオブジェクトの角速度を計算したい場合、オブジェクト回転量から計算することができます。

得られる結果は、回転軸とその軸周りの角速度です。

基本的な考え方は、前フレームと現在フレームの姿勢(クォータニオン)の差経過時間(デルタタイム)から求める流れです。

本記事では、オブジェクトの姿勢から現在の角速度をスクリプトから計算する方法について解説していきます。

動作環境
  • Unity2021.1.26f1

スポンサーリンク

サンプルスクリプト

最初に、オブジェクトの角速度と回転軸を求めるサンプルスクリプトを示します。

AngularVelocityCalculator.cs
using UnityEngine;

public class AngularVelocityCalculator : MonoBehaviour
{
    // 回転中しているかどうか
    public bool IsRotating { get; private set; }
    
    // 角速度[deg/s]
    public float AngularVelocity { get; private set; }

    // 回転軸
    public Vector3 Axis { get; private set; }

    // 前フレームの姿勢
    private Quaternion _prevRotation;

    private void Start()
    {
        _prevRotation = transform.rotation;
    }

    private void Update()
    {
        // 現在フレームの姿勢を取得
        var rotation = transform.rotation;

        // 前フレームからの回転量を求める
        var diffRotation = Quaternion.Inverse(_prevRotation) * rotation;
        // 回転した角度と軸(ローカル空間)を求める
        diffRotation.ToAngleAxis(out var angle, out var axis);

        // 回転角度が0以外なら回転しているとみなす
        IsRotating = !Mathf.Approximately(angle, 0);
        
        // 回転角度から角速度を計算
        AngularVelocity = angle / Time.deltaTime;
        // ローカル空間の回転軸をワールド空間に変換
        Axis = rotation * axis;

        // 前フレームの姿勢を更新
        _prevRotation = rotation;
    }
}

別のスクリプトからプロパティとして参照できるようにしました。

IsRotatingプロパティは回転しているか否か、AngularVelocityは回転軸周りの角速度、Axisは回転軸を返します。

例えば、ログ出力をする場合は次のようにして使います。

AngularVelocityLogger.cs
using UnityEngine;

public class AngularVelocityLogger : MonoBehaviour
{
    // あらかじめ計算スクリプトをアタッチしておく
    [SerializeField] private AngularVelocityCalculator _calculator;

    private void LateUpdate()
    {
        // 回転中かどうか
        if (_calculator.IsRotating)
        {
            // 回転している場合は、角速度と回転軸を出力
            print($"角速度 = {_calculator.AngularVelocity}, 回転軸 = {_calculator.Axis}");
        }
        else
        {
            // 回転していない場合
            print("回転していない");
        }
    }
}

これらのスクリプトは、以下動画のように角速度を計算したいオブジェクトにアタッチしてお使いください。

実行結果

次のように毎フレーム角速度のログが出力されるようになります。

回転軸を可視化すると次のようになります。

回転量の計算

角速度を計算するためには、前フレームからどれだけ回転したかの回転量を計算する必要があります。

これは、現在フレームにおける姿勢のクォータニオンから1フレーム前における姿勢のクォータニオンの逆回転を掛けることに相当します。

これは次のような処理で計算できます。

// 前フレームからの回転量を求める
var diffRotation = Quaternion.Inverse(_prevRotation) * rotation;

Quaternion.Inverseメソッドは、逆回転のクォータニオンを求めるメソッドです。

public static Quaternion Inverse(Quaternion rotation);

戻り値は、引数に指定されたクォータニオンの逆クォータニオンです。

また、この回転量はオブジェクトのローカル空間における回転操作となります。

回転軸と回転角度の計算

得られた回転量のクォータニオンから回転軸回転角度を求めます。

これは、Quaternion.ToAngleAxisメソッドから計算できます。

public void ToAngleAxis(out float angle, out Vector3 axis);

第1引数には回転角度を受け取るfloat型の変数第2引数には回転軸を受け取るVector3型の変数を指定します。

使い方は以下の通りです。

// 回転した角度と軸(ローカル空間)を求める
diffRotation.ToAngleAxis(out var angle, out var axis);

また、回転しているかどうかはangle変数が0かどうかで判断できます。

// 回転角度が0以外なら回転しているとみなす
IsRotating = !Mathf.Approximately(angle, 0);

角速度の計算

計算した回転量から角速度を計算します。角速度は、回転角度経過時間から近似的に算出できます。

// 回転角度から角速度を計算
AngularVelocity = angle / Time.deltaTime;

また、先述のコードで得られた回転軸はローカル空間での表現なので、オブジェクトの姿勢のクォータニオンを掛けてワールド空間に変換しています。

// ローカル空間の回転軸をワールド空間に変換
Axis = rotation * axis;

そのため、もしローカル空間における回転軸を求めたい場合はrotationを掛ける必要がありません。

さいごに

オブジェクトの角速度は、クォータニオンを用いた回転量から計算できます。

そして、Quaternion.ToAngleAxisメソッドを用いて角速度と回転軸という2つの値として角速度が得られます。

逆に角速度と回転軸を与えてオブジェクトを回転させたい場合は、Quaternion.AngleAxisメソッドを使って実現できます。この方法は以下記事で解説しています。

ご参考になれれば幸いです。

参考サイト

スポンサーリンク