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);

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

posted @ 2020-07-13 20:24  汐夜  阅读(2393)  评论(0编辑  收藏  举报