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);
四元数类中自带朝向旋转的方法,但需要先转换出目标向量对应的四元数。该方式可以实现匀速率旋转。