12.Quaternion四元数常用方法

12.3D数学-Quaternion四元数-四元数常用方法


12.1 知识点

主要学习内容

单位四元数

identity静态变量 代表单位四元数

identity静态变量代表单位旋转四元数。

//单位旋转(只读)。
print(Quaternion.identity);
testCube.rotation = Quaternion.identity;
//测试立方体无论当前旋转如何 都会恢复成每个轴都不旋转的(0,0,0)

//Object的Instantiate方法有重载 第三个参数可以传入四元数角度
Instantiate(testCube, Vector3.zero, Quaternion.identity);

插值运算

Lerp静态方法 四元数线性插值进行旋转

Lerp静态方法用于在两个四元数之间进行线性插值。

Lerp静态方法是Quaternion结构体提供的一个功能,用于在两个四元数之间进行线性插值。线性插值通常用于在两个值之间以线性方式进行平滑过渡。对于四元数,Lerp方法将在两个四元数之间进行插值,并返回一个介于它们之间的新四元数。此外,该方法还会对结果进行标准化处理,确保返回的四元数仍然代表一个单位旋转。

public static Quaternion Lerp(Quaternion a, Quaternion b, float t);
  • a:起始四元数。
  • b:目标四元数。
  • t:插值系数,表示在起始和目标之间的插值程度。它的取值范围通常在 [0, 1] 之间。当t为0时,返回a;当t为1时,返回b
Quaternion a = Quaternion.Euler(0, 45, 0); // 起始四元数
Quaternion b = Quaternion.Euler(0, 90, 0); // 目标四元数
float t = 0.5f; // 插值系数

Quaternion result = Quaternion.Lerp(a, b, t);

在这个示例中,我们从一个代表绕Y轴旋转45度的四元数 a,过渡到另一个代表绕Y轴旋转90度的四元数 b。通过将插值系数 t 设置为0.5,我们在这两个旋转之间进行了平滑的线性插值,并得到了一个新的四元数 result

Slerp静态方法 四元数球形插值进行旋转

Slerp静态方法用于在两个四元数之间进行球形插值。

// Update内
// 在四元数 a 与 b 之间按比率 t 进行球形插值。参数 t 限制在范围[0, 1] 内。
// 想让立方体A和立方体B慢慢变成和立方体target相同旋转
// 无限接近,先快后慢
// 传入当前旋转四元数和目标旋转四元数,每帧变化赋值给下一帧旋转四元数
A.transform.rotation = Quaternion.Slerp(A.transform.rotation, target.rotation, Time.deltaTime);
// 匀速变化,time>=1到达目标
// 定死开始旋转四元数和目标旋转四元数,累加事件
time += Time.deltaTime;
// 开始旋转四元数在Start()内 start = B.transform.rotation 赋好值
B.transform.rotation = Quaternion.Slerp(start, target.rotation, time);

向量指向转四元数LookRotation

LookRotation静态方法 获得向量指向转四元数

LookRotation静态方法用于使用指定的forward和upwards方向创建旋转。

// 目标是想让A立即转向看向B
// 先让lookA和lookB位置相减算出lookAB向量,传入LookRotation静态方法中,获得返回的四元数赋值给lookA
Quaternion q = Quaternion.LookRotation(lookB.position - lookA.position);
lookA.rotation = q;
// 资源实现的效果和Transform中的静态方法LookAt方法类似

总结


12.2 知识点代码

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

public class Lesson12_3D数学_Quaternion四元数_四元数常用方法 : MonoBehaviour
{
    //测试立方体
    public Transform testCube;

    //三个立方体 想让立方体A和立方体B慢慢变成和立方体target相同旋转
    public Transform target;
    public Transform A;
    public Transform B;

    private Quaternion start;
    private float time;

    public Transform lookA;
    public Transform lookB;
    
    void Start()
    {
        #region 知识点一 单位四元数

        //identity静态变量 代表单位四元数
        //单位旋转(只读)。
        print(Quaternion.identity);
        testCube.rotation = Quaternion.identity;
        //测试立方体无论当前旋转如何 都会恢复成每个轴都不旋转的(0,0,0)

        //Object的Instantiate方法有重载 第三个参数可以传入四元数角度
        Instantiate(testCube, Vector3.zero, Quaternion.identity);

        #endregion

        #region 知识点二 插值运算
        start = B.transform.rotation;
        #endregion


    }
    
    void Update()
    {
        #region 知识点二 插值运算

        //Lerp静态方法 四元数线形插值进行旋转
        //在 a 和 b 之间插入 newThread,然后对结果进行标准化处理。参数 newThread 被限制在[0, 1] 范围内。
        //四元数一般使用球形插值 线性插值的用法和球形插值相同 略

        //Slerp静态方法 四元数球形插值进行旋转
        //在四元数 a 与 b 之间按比率 newThread 进行球形插值。参数 newThread 限制在范围[0, 1] 内。
        //想让立方体A和立方体B慢慢变成和立方体target相同旋转
        //无限接近 先快后慢
        //传入当前旋转四元数好目标旋转四元数 每帧变化赋值给下一帧旋转四元数
        A.transform.rotation = Quaternion.Slerp(A.transform.rotation, target.rotation, Time.deltaTime);
        //匀速变化 testNum>=1到达目标
        //定死开始旋转四元数和目标旋转四元数 累加事件
        time += Time.deltaTime;
        //开始旋转四元数在Start()内 start = B.transform.rotation赋好值
        B.transform.rotation = Quaternion.Slerp(start, target.rotation, time);

        #endregion

        #region 知识点三 向量指向转四元数LookRotation

        //LookRotation静态方法 获得向量指向转四元数
        //使用指定的 forward 和 upwards 方向创建旋转。
        //目标是想让A立即转向看向B
        //先让lookA和lookB位置相减算出lookAB向量,传入LookRotation静态方法中,获得返回的四元数赋值给lookA
        Quaternion q = Quaternion.LookRotation(lookB.position - lookA.position);
        lookA.rotation = q;
        //资源实现的效果和Transform中的静态方法LookAt方法类似

        #endregion
    }
}

12.3 练习题

利用四元数的LookRotation方法,实现LookAt的效果

假设Transform类没有LookAt方法,创建Tools脚本,为Transform类提供效果和LookAt方法效果一样的拓展方法

// 自己写的看向目标的方法
public static void MyLookAt(this Transform obj, Transform target)
{
    // 算出目标和自己的向量
    Vector3 vec = target.position - obj.position;
    // 用LookRotation得到转向的四元数赋值给自己
    obj.transform.rotation = Quaternion.LookRotation(vec);
}

调用自己写的拓展方法看向B

lookA.MyLookAt(lookB);

将之前摄像机移动的练习题中的LookAt换成LookRotation实现,并且通过Slerp来缓慢看向玩家

摄像机先快后慢旋转看向目标

// 用目标的位置 减去 摄像机的位置 得到新的面朝向向量四元数
targetQ = Quaternion.LookRotation(target.position - this.transform.position);
// 调用球形插值 传入当前摄像机当前旋转四元数、面朝向向量四元数、以及旋转速度 赋值给下一帧摄像机当前旋转
this.transform.rotation = Quaternion.Slerp(this.transform.rotation, targetQ, Time.deltaTime * roundSpeed);

摄像机匀速旋转看向目标

// 当发现目标对象位置改变时
if (targetQ != Quaternion.LookRotation(target.position - this.transform.position))
{
    // 重新用目标的位置减去摄像机的位置计算出新的面朝向向量四元数
    targetQ = Quaternion.LookRotation(target.position - this.transform.position);
    // 清空累加时间 不然时间一直大于1 会一直盯着目标看 不能达到匀速旋转看向目标的效果
    roundTime = 0;
    // 重置摄像机开始旋转位置
    startQ = this.transform.rotation;
}

// 累加时间
roundTime += Time.deltaTime;

// 传入发现目标对象位置改变后定死的摄像机开始旋转位置,目标旋转位置,和累加的时间
this.transform.rotation = Quaternion.Slerp(startQ, targetQ, roundTime * roundSpeed);

12.4 练习题代码

Lesson12_练习题

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

//这个脚本将要挂载到摄像机上 实现摄像机跟随
public class Lesson12_练习题 : MonoBehaviour
{    
    #region 练习题一
    //利用四元数的LookRotation方法,实现LookAt的效果

    public Transform lookA;
    public Transform lookB;
    
    void Update()
    {
        //调用自己写的拓展方法看向B
        lookA.MyLookAt(lookB);
    }
    #endregion

    #region 练习题二
    //将之前摄像机移动的练习题中的LookAt换成LookRotation实现,并且通过Slerp来缓慢看向玩家

    //目标对象
    public Transform target;
    //目标旋转角度四元数
    private Quaternion targetQ;
    //旋转速度
    public float roundSpeed;
    //旋转累加事件
    private float roundTime;
    //开始时的旋转四元数
    private Quaternion startQ;

    private void LateUpdate()
    {
        //摄像机先快后慢旋转看向目标
        ////用目标的位置 减去 摄像机的位置 得到新的面朝向向量四元数
        //targetQ = Quaternion.LookRotation(target.position - this.transform.position);
        ////调用球形插值 传入当前摄像机当前旋转四元数、面朝向向量四元数、以及旋转速度 赋值给下一帧摄像机当前旋转
        //this.transform.rotation = Quaternion.Slerp(this.transform.rotation, targetQ, Time.deltaTime * roundSpeed);

        //摄像机匀速旋转看向目标
        ////当发现目标对象位置改变时
        //if (targetQ != Quaternion.LookRotation(target.position - this.transform.position))
        //{
        //    //重新用目标的位置减去摄像机的位置计算出新的面朝向向量四元数
        //    targetQ = Quaternion.LookRotation(target.position - this.transform.position);
        //    //清空累加时间 不然时间一直大于1 会一直盯着目标看 不能达到匀速旋转看向目标的效果
        //    roundTime = 0;
        //    //重置摄像机开始旋转位置
        //    startQ = this.transform.rotation;
        //}

        ////累加时间
        //roundTime += Time.deltaTime;

        ////传入发现目标对象位置改变后定死的摄像机开始旋转位置,目标旋转位置,和累加的时间
        //this.transform.rotation = Quaternion.Slerp(startQ, targetQ, roundTime * roundSpeed);

    }

    #endregion
}

Tools

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

public static class Tools
{
    //自己写的看向目标的方法
    public static void MyLookAt(this Transform obj, Transform target)
    {
        //算出目标和自己的向量
        Vector3 vec = target.position - obj.position;
        //用LookRotation得到转向的四元数赋值给自己
        obj.transform.rotation = Quaternion.LookRotation(vec);
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏