一、向量的点乘和叉乘
Unity中常常需要判断一个物体相对于另外一个物体的方位,这里假设两个物体的y轴坐标都为0。抛出如下问题。
假设A是一个巡逻的怪物,B是玩家,请问如何判断B相当于A的位置?
1.点乘
向量的点乘就是将两个向量互相相乘,举个例子A(x1,y1,z1),B(x2,y2,z2),那么A和B点乘的结果是一个新的向量C(x1*x2,y1*y2,z1*z2)
Unity中提供了点乘的方法Vector3.Dot方法,通过向量点乘可以求两个向量之间的夹角,这个夹角的意思如下图
通过点乘就可以获得角度A,具体如下代码所示
result = Vector3.Dot(this.transform.forward, (target.position - this.transform.position).normalized);
print("角度:" + Mathf.Acos(result) * Mathf.Rad2Deg);
这里的this就是A物体,target就是B物体。值得注意的是,求点乘需要使用单位向量,避免结果出错,之后再利用反三角函数得出弧度后再转成角度即可。注意这里得出的角度是A物体的面朝向和向量AB之间的夹角。
这是原理,实际上Unity中提供了求两个向量之间夹角的函数即Vector3.Angle,所以上述代码可以简化成如下
print("角度2:" + Vector3.Angle(this.transform.forward, target.position - this.transform.position));
虽然方法很简单,但是希望各位懂得原理。
这样就可以回答开始提出的问题,可以说B相对于A的正前方偏移了X度。那么向左偏移还是向右便偏移呢?
这就需要叉乘了。
2.叉乘
同样简单叙述下向量A和向量B叉乘的结果:(y1 * z2 - y2 * z1, x1 * z2 - x2 * z1, x1 * y2 - x2 * y1),依然是一个向量,请注意以上是A×B的结果,A×B =-B×A,这点很重要。
叉乘有什么几何意义呢?首先要明白A叉乘B得到的向量是什么,它是A向量和B向量组成的平面的法线向量,就是垂直于这个平面的向量,把开始的问题放到三维空间中
紫色的向量C就是A的面朝向向量A.transform.forward和向量AB的叉乘得到的法线向量。这个法线向量的Y值是大于0的,这个时候就可以判断出B在A的右侧,如果C的Y 值小于0,那么B就在A的左侧。
同样的Unity中也有自带的求叉乘的方法Vector3.Cross。根据上述说的原理,可以这样写
C = Vector3.Cross(A.position, B.position);
print(D.y > 0 ? "B在A右侧" : "B在A左侧");
此API直接两个物体的位置坐标就可以了。
至此先求点乘再求叉乘,可以这么描述B相对于A的位置,B在A正前方向右偏移X度的方向上,至于距离,直接用Vector3.Distance求就可以了。
点乘和叉乘的作用还有很多,这里先留白,后面遇到了还会来补充。
二.四元数
1.为什么用四元数
Unity编辑界面物体transform的rotation几个值是物体绕几个轴的旋转的欧拉角。欧拉角是存在问题的。
首先是物体旋转的角度不是唯一的。
把一个物体的Rotation的Y设置成45和405物体呈现出来的姿态是一样的。假设要实现一个效果,物体绕Y轴旋转的角度等于45度就爆炸。条件是不可以直接写this.transform.rotation.eulerAngles.y =45。因为角度是405度也要爆炸。这只是一个简单的例子,当然可以完善,但是用四元数就真的可以写成 = 45.
其次就是万向节死锁问题。这个问题我不好描述,你可以去搜一搜这个问题所在。你可以复现这个问题。
创建一个立方体,把它的欧拉角设置为(90,0,0),然后再去拖动欧拉角的y值,你会发现此时物体并不是绕着Y轴旋转,而是绕着Z轴转。这就是万向节死锁的一个复现。具体的原理你可以去别的博客看一下。四元数就可以解决这个问题。
四元数就可以解决这两个问题。
2.四元数的表示
Unity中四元数是这样表示的,物体的任何形式的旋转都可以归纳为绕着一个轴旋转X度。假设物体绕着轴n(x,y,z)旋转β度。那么四元数可以这样表示[x*sin(β/2),y*sin(β/2),z*sin(β/2),cos(β/2)]。这其中的原理你可以找论文去看。
我们当然不可能在Unity中这样声明一个四元数,可以使用这样的方法声明一个四元数
Quaternion q = Quaternion.AngleAxis(60, Vector3.up);
AngleAxis是轴角对初始化。上面的代码的意思是这个四元数让物体绕自身的Y轴旋转60度得到的四元数。
此外还可以通过欧拉角和四元数的转换,如下面的代码
Quaternion q = Quaternion.Euler(60, 0, 0);
这句代码的意思就是把物体绕X轴旋转60度的欧拉角转换为了四元数。
3.四元数的计算
1.两个四元数相乘得到一个新的四元数,这就代表着可以通过四元数相乘的方法让物体旋转起来
this.transform.rotation *= Quaternion.AngleAxis(20, Vector3.up);
这句代码放在Update函数中物体就可以真正绕着自身的Y轴转。无论物体的初始欧拉角是什么样子,物体此时始终会绕着自己的Y轴旋转,这就解决了万向节死锁的问题。
此外,物体旋转时,观察Inspector界面transform的rotation的y值,可以看到值一直在-180到180之间变化,这就解决了物体旋转的角度不是唯一的问题。比如物体旋转角度作为45度就直接爆炸,那么可以通过四元数将物体旋转,然后判断它的欧拉角的y值,等于45度直接爆炸,是没有问题的。
2.四元数乘向量
这是一个很重要的东西。用一个四元数乘以向量返回的是一个新的向量,注意顺序,必须是四元数乘向量。
Vector3 v = Vector3.forward;
v = Quaternion.AngleAxis(45, Vector3.up) * v;
这两句代码意思就是把向量(0,0,1)绕着世界坐标轴y轴旋转45度,得到的向量就是(0.707,0,0.707),0.707就是二分之根号二。想一下把一个向量旋转有什么作用
这就很牛逼了。比如我要发射一枚rpg,我希望发射的角度是斜上方,那么这个时候就可以用rpg的面朝向乘以一个偏移的四元数,那么就可以得到一个斜向的发射方向。还不牛逼吗。
暂时就说到这里,当然还有很多数学,后面遇到再来补充。