这篇内容将与unity中移动旋转的控制密切相关,有关移动旋转。
主要涉及到的类:Vector3,Mathf,Quaternion,Transform。
1.三角函数
其实关于三角函数的使用并不会很常见,因为unity已经给我们提供了很多方便计算向量夹角、角度范围的方法,可以直接调用,除非我们想自己去封装一些特殊的需求,否则用unity提供给我们的方法就很简便了。但是只有理解了原理才能够明白何时怎么用,所以还是要看看其中的数学原理。
(1).角度的度量方式
- 角度:弧长等于圆周长1/360时对应的角度为1°;
- 弧度:弧长等于半径时为1弧度,比如π就是半圈,周长为2πr;
- 角度和弧度之间的转换:
(2).三角函数:
(3).反三角函数:
2.向量
(1).向量常用属性:
Vector3.magnitude
向量长度Vector3.sqrMagnitude
向量的平方长度
向量 v 的大小以Mathf.Sqrt(Vector3.Dot(v, v)) 方式进行计算。 但是,Sqrt 计算相当复杂, 执行时间比普通算术运算要长。计算平方数代替使用 magnitude 属性要快得多 - 计算基本相同,只是消除了执行缓慢的 Sqrt 调用。如果您只将大小用于比较距离的目的,则也可以将平方数与距离的平方进行比较, 因为这种比较也会给出相同的结果。Vector3.normalized
标准化向量(单位向量)
(2).点乘: Vector3.Dot
public static float Dot (Vector3 lhs, Vector3 rhs);
应用:
(3).叉乘:Vector3.Cross
public static Vector3 Cross (Vector3 lhs, Vector3 rhs);
应用:
a.创建垂直于lhs和rhs构成的平面的向量=>自己创建坐标系。
b.判断lhs和rhs的相对位置=>根据正负判断顺逆时针关系。
(4).Vector3.Angle 计算向量间夹角
public static float Angle (Vector3 from, Vector3 to);
返回的角度为两个向量之间的无符号角度。也就是说,使用两个向量之间的两个可能角度中的较小者。结果永远不会超过 180 度。
这要比我们自己用点乘和三角函数求夹角快多了。
3.欧拉角transform.eulerAngles
欧拉角在航空领域叫做导航角,就像下图这个飞机,可以抬头低头(绕x轴),左右摇摆(绕z轴),改变前进方向(绕y轴),非常便于理解。
(1).优点:
• 仅使用三个数字表达方位,占用空间小。
• 沿坐标轴旋转的单位为角度,符合人的思考方式。
•任意三个数字都是合法的,不存在不合法的欧拉角。
(2).缺点:
a.方位的表达方式不维一:
• 对于一个方位,存在多个欧拉角描述,因此无法判断多个欧拉角代表的角位移是否相同。正如官方描述的这样:
以欧拉角表示的旋转(以度为单位)。 x、y 和 z 角表示一个围绕 Z 轴旋转 z 度、围绕 X 轴旋转 x 度、围绕 Y 轴旋转 y度的旋转。 仅使用该变量读取角度和将角度设置为绝对值。不要增大角度,因为当角度超过 360 度时,操作将失败。 请改用Transform.Rotate。
• 例如:
角度0,5,0与角度0,365,0
角度0,-5,0与角度0,355,0
角度250,0,0与角度290,180,180
b.万向节死锁:
• 物体沿X轴旋转±90度,自身坐标系Z轴与世界坐标系Y轴将重合,此时再沿Y或Z轴旋转时,将失去一个自由度。
• 在万向节死锁情况下,规定沿Y轴完成绕竖直轴的全部旋转,即此时Z轴旋转为0。
4.四元数:Quaternion qt = transform.rotation;
(1).优点:
避免万向节死锁
• this.transform.rotation *= Quaternion.Euler(0, 1, 0); 可使物体沿自身坐标Y轴旋转
• this.transform.Rotate(Vector3 eulerAngles)内部就是使用四元数相乘实现
(2).缺点:
• 难于使用,不建议单独修改某个数值。
• 存在不合法的四元数。
(3).基本使用和运算:
a.从四元数中读取欧拉角:Quaternion.eulerAngles
b.四元数左乘向量,表示将该向量按照四元数表示的角度旋转:
Quaternion.Euler : public static Quaternion Euler (float x, float y, float z);
返回一个围绕 Z 轴旋转 z 度、围绕 X 轴旋转 x 度、围绕 Y 轴旋转 y 度的旋转。
eg:
// A rotation 30 degrees around the y-axis
Quaternion rotation = Quaternion.Euler(0, 30, 0);
c.四元数之间相乘
• 两个四元数相乘可以组合旋转效果。
• 例如:
Quaternion rotation01 =Quaternion.Euler(0, 30, 0) * Quaternion.Euler(0, 20, 0);
Quaternion rotation02 = Quaternion.Euler(0, 50, 0);
rotation01 与 rotation02 相同。
5.重要应用实例:
在吃鸡、CS中手雷都是致命武器,但是我们会发现不同情况下手雷的伤害却不同,可能会和爆炸点距离、掩体、装备好坏有关,这里我们就以处理掩体和手雷爆炸造成伤害的方式,我们研究的当然是最尴尬的情况:
也就是要判断玩家是否完全躲在了掩体后面,显然这就和切点c的位置密切相关了,如果c和炸弹的连线会经过掩体,那么玩家就完全躲了进去。
那么问题就变成了如何计算切点c的位置:
a.我们已知的:玩家坐标,炸弹坐标,玩家半径
b.可以通过向量运算求出来的:
//向量a:玩家指向炸弹的向量
Vector3 a = 炸弹.tranform.position - 玩家.tranform.position;
//向量b :向量a在玩家部分的向量
Vector3 b = a.normalized * 玩家半径;
//图中的夹角:利用反余弦(这时可就用不了Vector3.angle了!!!)
float angle = Mathf.Acos( 玩家半径 / a.magnitude ) * Mathf.Red2Deg;
c.最精髓的一步来了:
切点C的坐标:就是向量b旋转到圆心和切点方向时加上玩家坐标(向量是从玩家指向外界的)。
Vector3 c = 玩家.tranform.position + Quaternion.Euler(0,-angle,0) * b;
当然这只是图示的情况,如果露出的在下面那就是:
Vector3 c = 玩家.tranform.position + Quaternion.Euler(0,angle,0) * b;