zoukankan      html  css  js  c++  java
  • Unity 以一定角速度转向动态目标的旋转方式对比

    1.欧拉角旋转

    public void Rotate(Vector3 eulers, [DefaultValue("Space.Self")] Space relativeTo);

    就容易想到的就是transform.Rotate方法:

     1 RotationObj.transform.Rotate(Vector3.up * Palstance * Time.deltaTime); 

    其中Palstance代表角速度。

    但很快就会发现这个方法有2个很大的缺陷:

    ①需要利用cross值(叉积)来手动判断是绕旋转轴逆时针还是顺时针旋转

    如果叉积为正,说明目标体在旋转体右侧,需顺时针旋转;

    如果叉积为负,说明目标体在旋转体左侧,需逆时针旋转

    具体判断如下:

     1         var cross = Vector3.Cross(RotationObj.transform.forward, offset).y;
     2         if (cross > 0)
     3         {
     4             //
     5             if (Palstance < 0)
     6             {
     7                 Palstance = -Palstance;
     8             }
     9         }
    10         else if (cross < 0)
    11         {
    12             //
    13             if (Palstance > 0)
    14             {
    15                 Palstance = -Palstance;
    16             }
    17         }

    其中offset代表目标体与旋转体坐标间的向量。

    ②难以判断何时应该停止旋转,且角速度过大时很容易造成在到达目标向量附近来回鬼畜旋转

    一般的考虑是,当旋转体的前方向向量transform.forward与offset小于一定阈值时停止旋转,例如:

    1         var angle = Vector3.Angle(RotationObj.transform.forward, offset);
    2         if (angle < .1f) 
    3             return ;

    但当角速度过快时,很容易错过[0,0.1]这一角度范围,但如果把范围设置过大,有没办法精准对齐,于是就造成了在目标向量附近来回鬼畜旋转的状况;

    当然了,也可以用一种非常生硬的方式来解决:

    1         //基于当前角速度一帧内最大的旋转角度
    2         if (angle < Palstance * Time.deltaTime)
    3         {
    4             RotationObj.transform.forward = offset;
    5         }

    即设置另一个阈值范围(并且这个阈值范围最好和当前角速度正相关,可以计算出基于当前角速度一帧内最大的旋转角度进行设置),当小于该阈值范围时直接瞬切,因为本来就是在一帧内的角度运动,所以不会有任何违和感。

    也可以考虑将判定范围与该旋转阈值设置为同一个。完整旋转方式如下:

     1         //基于当前角速度一帧内最大的旋转角度
     2         if (angle < Palstance * Time.deltaTime)
     3         {
     4             RotationObj.transform.forward = offset;
     5             return;
     6         }
     7 
     8         var cross = Vector3.Cross(RotationObj.transform.forward, offset).y;
     9         if (cross > 0)
    10         {
    11             //
    12             if (Palstance < 0)
    13             {
    14                 Palstance = -Palstance;
    15             }
    16         }
    17         else if (cross < 0)
    18         {
    19             //
    20             if (Palstance > 0)
    21             {
    22                 Palstance = -Palstance;
    23             }
    24         }
    25         RotationObj.transform.Rotate(Vector3.up * Palstance * Time.deltaTime);

    上面的方式经过调整后虽然能够实现准确转向,但看上去并不简单直接,那有没有更简洁快速的旋转方式呢。

    2.插值旋转

    Lerp(a,b,t);

    旋转朝向实际上可以认为是对transform.forward进行关于角速度的插值变化:

     1 RotationObj.transform.forward = Vector3.Lerp(RotationObj.transform.forward, offset, Time.deltaTime * Palstance / angle).normalized; 

    Time.deltaTime/(angel/Palstance)=Time.deltaTime * Palstance / angle;

    利用当前角度与角速度相除计算出当前帧率下的预计旋转时间,随后用当前帧率与预计旋转时间的比值来对两个向量进行插值。

    这种方法非常简单,但也有一个问题是没办法做到匀速旋转,角色的朝向,当前帧速率和角度可能会随时发生变化。

    3.四元数旋转

    1         Quaternion q = Quaternion.LookRotation(offset);
    2         RotationObj.transform.rotation = Quaternion.RotateTowards(RotationObj.transform.rotation, q, Palstance * Time.deltaTime);

    四元数类中自带朝向旋转的方法,但需要先转换出目标向量对应的四元数。该方式可以实现匀速率旋转。

  • 相关阅读:
    11.菜单(一)
    线性表之顺序存储详解
    SVN 撤回已提交的代码
    线性表1
    顶层父类
    异常类之派生类
    new和delete重载
    异常类之基类
    Qt中多线程问题
    智能指针实例
  • 原文地址:https://www.cnblogs.com/koshio0219/p/13295175.html
Copyright © 2011-2022 走看看