栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 游戏开发 > Unity3D

Unity Quaternion四元数 常用API解析 和 旋转插值动画实现

Unity3D 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Unity Quaternion四元数 常用API解析 和 旋转插值动画实现

Unity Quaternion四元数 常用API解析 和 旋转插值动画实现
  • 一、Quaternion常用API
    • Quaternion.FromToRotation(Vector3 fromDirection, Vector3 toDirection)
    • Quaternion.LookRotation(Vector3 forward, Vector3 upwards = Vector3.up)
  • 二、旋转插值与动画实现
    • Quaternion.Lerp(Quaternion a, Quaternion b, float t)
    • Quaternion.Slerp(Quaternion a, Quaternion b, float t)
    • Quaternion.RotateTowards(Quaternion from,Quaternion to,float maxDegreesDelta)
    • Quaternion AngleAxis(float angle, Vector3 axis)
  • 三、结语

一、Quaternion常用API Quaternion.FromToRotation(Vector3 fromDirection, Vector3 toDirection)

官方解析:Creates a rotation which rotates from fromDirection to toDirection
中文意思:创建一个从form方向转到to目标方向所需要的旋转
设想一个这样的场景,我们用Quaternion.FromToRotation()这个API,把下图主物体(m_MainTran) 的z轴(蓝色轴) 朝向目标物体(m_TargetTran)应该怎么办呢。

代码

            //求出目标朝向
            Vector3 targetDir = m_TargetTran.position - m_MainTran.position;
            
            //求出m_MainTran.forward旋转到目标方向需要的旋转四元数
            Quaternion  q = Quaternion.FromToRotation(m_MainTran.forward, targetDir);
            
            //把求出来的旋转四元数应用到 forward 方向
            Vector3 finalDir = q * m_MainTran.forward; 
            
            //最后让主目标的 forward 朝向计算出来的方向
            m_MainTran.forward = finalDir;

解析都在注释了。接下来看下效果

同样的,如果我们想让主目标的x轴朝向目标,即right方向。可以这样写

            //求出目标朝向
            Vector3 targetDir = m_TargetTran.position - m_MainTran.position;

            //求出m_MainTran.right旋转到目标方向需要的旋转四元数
            Quaternion q = Quaternion.FromToRotation(m_MainTran.right,targetDir);

            //把求出来的旋转四元数应用到 right 方向
            Vector3 finalDir = q * m_MainTran.right;

            //最后让主目标的 right 朝向计算出来的方向
            m_MainTran.right = finalDir;

效果图

这在2D游戏很有用。因为2D游戏我们一般是让物体的x或y朝向目标,而不是z轴

Quaternion.LookRotation(Vector3 forward, Vector3 upwards = Vector3.up)
  • 官方解析:Creates a rotation with the specified forward and upwards directions
  • 中文意思:创建一个旋转。使得目标的正方向(z轴)指向目标forward.
  • tip:看向目标时y轴可以在z轴上方,也可以在下方。这里用第2个参数控制,不填的话默认是Vector.up上方。想改变方向可以用Vector.down

同样的场景,我们用Quaternion.LookRotation()来让目标朝向目标方向,上代码

            //求出目标方向
            Vector3 targetDir = m_TargetTran.position - m_MainTran.position;
            
            //计算出z轴朝向目标方向需要的旋转四元数
            Quaternion rotation =  Quaternion.LookRotation(targetDir,Vector3.up);
            
            //让m_MainTran.rotation等于求出的旋转
            m_MainTran.rotation = rotation;

效果图

可以看到,主物体也成功的朝向了目标。这个方法求出来的旋转表示Z轴朝向目标方向需要的旋转。假如我们想让X轴朝向目标方向怎么办呢。

  • 可以在求出结果后,让自己right等于自己的forward。如以下代码
            Vector3 targetDir = m_TargetTran.position - m_MainTran.position;
            Quaternion rotation =  Quaternion.LookRotation(targetDir,Vector3.up);
            m_MainTran.rotation = rotation;
            
            //上面的代码会使z轴朝向目标。 这里在这基础上让 right 朝向 forward 就可以了
            m_MainTran.right = m_MainTran.forward;

效果图

二、旋转插值与动画实现 Quaternion.Lerp(Quaternion a, Quaternion b, float t)
  • 官文解析:Interpolates between a and b by t and normalizes the result afterwards. The parameter t is clamped to the range [0, 1]
  • 中文意思:在a和b两个四元数之间进行插值,插值范围t =【0~1】

我们可以用Quaternion.Lerp()来实现旋转动画,注意这里填入的参数是四元数,我们可以用上面的Quaternion.FromToRotation()或Quaternion.LookRotation()来求出我们要的参数。

接下来我们来实现,主目标的Z轴在2秒内转向目标方向

    private bool isRotating = false;
    private float curTime = 0;
    private float AllTime = 3;

    private Quaternion oldQ;
    private Quaternion targetQ;
    
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            isRotating = true;
            curTime = 0;
            
            //当前旋转
            oldQ = m_MainTran.rotation;
            //目标旋转
            targetQ = Quaternion.LookRotation(m_TargetTran.position - m_MainTran.position);
        }

        if (isRotating)
        {
            curTime += Time.deltaTime; 
            float t = curTime / AllTime;
            t = Mathf.Clamp(t, 0, 1);
            
            //用t进行插值
            Quaternion lerpQ = Quaternion.Lerp(oldQ,targetQ,t);
            //设置到目标旋转
            m_MainTran.rotation = lerpQ;

            Debug.Log($"{GetType()} curT:{t}");
            if (t >= 1)
            {
                isRotating = false;
            }
        }
    }

效果图

有时我们需要的是其他轴,比如X轴朝向目标,那该怎么做呢。接下来我们来实现一下,上代码

    private bool isRotating = false;
    private float curTime = 0;
    private float AllTime = 3;

    private Vector3 oldDir;
    private Quaternion oldQ;
    private Quaternion targetQ;
    
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            isRotating = true;
            curTime = 0;
            
            //记录起始点的朝向
            oldDir = m_MainTran.right;
            //起始插值参数(对于oldDir方向来说,起始不需要旋转。所以这里是Quaternion.identity)
            oldQ = Quaternion.identity;
            //目标插值参数
            targetQ = Quaternion.FromToRotation(m_MainTran.right, m_TargetTran.position - m_MainTran.position);
        }

        if (isRotating)
        {
            curTime += Time.deltaTime; 
            float t = curTime / AllTime;
            t = Mathf.Clamp(t, 0, 1);
            
            //用t进行插值
            Quaternion lerpQ = Quaternion.Lerp(oldQ,targetQ,t);
            //设置到目标旋转
            m_MainTran.right = lerpQ * oldDir;

            Debug.Log($"{GetType()} curT:{t}");
            if (t >= 1)
            {
                isRotating = false;
            }
        }
    }

效果图:
注释都加在代码上了。按下A键,可以看到我们的X轴转向了目标

Quaternion.Slerp(Quaternion a, Quaternion b, float t)

这个是球型插值,动画效果可能较Quaternion.Lerp()更好一点。
具体用法和Quaternion.Lerp()一样,只需要把上面代码里的Lerp()改成Slerp()既可以看到效果。这里就不罗列出来了。大伙可以自行替换看看。

Quaternion.RotateTowards(Quaternion from,Quaternion to,float maxDegreesDelta)
  • 官方解析:Rotates a rotation from towards to
  • 中文意思:将旋转从 from 转向 to。 旋转的角度为 maxDegreesDelta ,但最后的旋转不会超过to. 所以这里是理论最大旋转角度。

可以看下源码,其实这里最后也是用Quaternion.SlerpUnclamped()来计算的。先求出总角度,然后把
maxDegreesDelta / 总角度 作为插值t

    public static Quaternion RotateTowards(
      Quaternion from,
      Quaternion to,
      float maxDegreesDelta)
    {
      float num = Quaternion.Angle(from, to);
      return (double) num == 0.0 ? to : Quaternion.SlerpUnclamped(from, to, Mathf.Min(1f, maxDegreesDelta / num));
    }

有了这个方法,我们可以指定旋转角度来转向目标。接下来我们来试试,上代码

    private bool isRotating = false;
    private float perAngel = 15;//每秒转15度
    private Quaternion targetQ;
    
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            isRotating = true;
            curTime = 0;
            
            //目标插值参数
            targetQ = Quaternion.LookRotation(m_TargetTran.position - m_MainTran.position);
        }
        
        if (isRotating)
        {
            //计算出每帧的旋转
            Quaternion rotQ = Quaternion.RotateTowards(transform.rotation,targetQ,perAngel * Time.deltaTime);
            //设置到目标旋转
            m_MainTran.rotation = rotQ;
            
            if (m_MainTran.rotation.Equals(targetQ))
            {
                isRotating = false;
                Debug.Log($"{GetType()} exit rot");
            }
        }
    }

效果图:

Quaternion AngleAxis(float angle, Vector3 axis)
  • 官方解析:Creates a rotation which rotates angle degrees around axis

  • 中文意思:创建一个旋转。代表以axis方向作为旋转轴,旋转angle角度

  • 用这个方法,我们可以让我们的方向沿着任意轴旋转任意角度,精确的按我们想要的效果来旋转。
    比如在2D游戏中,我们一般是沿着Vector.forward来旋转;
    在3D空间中,我们应该沿着哪个方向来旋转呢?

我们可以用叉乘来求出旋转轴。叉乘的数学意义:Vector c = Vector3.Cross(a,b); 求出来的向量c垂直于向量a与b形成的平面。这个向量c就是我们要的旋转轴。

接下来我们使用Quaternion.AngleAxis()来实现前面的旋转动画。上代码

    private bool isRotating = false;
    private float perAngel = 15;//每秒转15度
    private Vector3 rotAxis;
    
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            isRotating = true;

            //求出旋转轴
            rotAxis = Vector3.Cross(m_MainTran.forward,m_TargetTran.position - m_MainTran.position);
        }
        
        if (isRotating)
        {
            //计算出每帧的旋转
            Quaternion rotQ = Quaternion.AngleAxis(perAngel * Time.deltaTime,rotAxis);
            //应用旋转
            m_MainTran.forward = rotQ * m_MainTran.forward;
            
            //如果旋转到只差2度,就旋转到目标方向,并退出旋转动画
            if (Vector3.Angle(m_MainTran.forward,m_TargetTran.position - m_MainTran.position) <= 2)
            {
                m_MainTran.forward = m_TargetTran.position - m_MainTran.position;
                isRotating = false;
                Debug.Log($"{GetType()} exit rot");
            }
        }
    }

效果图:

可以看到,主物体也成功的转向目标了。

三、结语
  • 四元数是一个比较抽象的概念,但是掌握上面的API,就可以随心所欲的转转转了~
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/900828.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号