很多小老弟面试时啊,经常被面试官问到这个问题,说实在的,答不上来,
当时空气就瞬间凝固了。。
今天我们就来针对这个问题好好唠一唠,点乘和叉乘使用场景是什么,到底该怎样使用点乘叉乘。
其实我们如果进入游戏制作行业,就会经常用到点乘叉乘了,
- 比如怪物移动、追踪主角时,判断怪物的是否面向主角、
- 怪物从自然的向前追,到拐弯搜到主角,这个怪物拐弯身体应旋转多少度、顺时针转还是逆时针转?
🟥 点乘计算面向还是背向
我们先来看第一个问题:
怪物移动、追踪主角时,判断怪物的是否面向主角
1️⃣ 面向背向的定义
我们先来声明一下定义哈
这儿的面向,并不是两个人面对面,而是比如说两个人向前的夹角在0-90度之间。
背向,也不是背靠背,而是方向夹角在90-180度之间。
比如说下图面向:
背向:
这儿的面向背向属实太忽悠了
2️⃣ 《第五人格》应用案例
比如这个《第五人格》红蝶技能:
离魄移魂:红蝶进入生成状态变为般若相,轻跃向空中俯瞰周身环境,使用此技能可增加刹那生灭的释放距离,但红蝶畏惧他人视线,被求生者注视面部时会变成惊慌相,无法释放刹那生灭。
那么就需要在红蝶进入般若相时,当和玩家在一定距离内,
要检测玩家是否背向自己(背向是方向夹角在90-180之间),
决定自己面部表情、能否释放攻击技能。
3️⃣ 推导过程
根据我们高中学的公式 :a · b = |a|*|b| cosθ,θ∈(0,180)
那么:
- θ∈(0,90)即面向,点乘结果>0
- θ∈(90,180)即背向, 点乘结果<0
那么我们只要判断点乘的结果>0,还是<0,就知道面向还是背向了。
知识点:
点乘的结果是float类型
叉乘的结果还是向量
4️⃣ Unity代码
Unity中代码如下:
public Transform trans00; public Transform trans01; void Update() { float result = Vector3.Dot(trans00.forward, trans01.forward); print(result); if (result == 1) { print("两物体同向平行"); } else if (result == -1) { print("两物体反向平行"); } else if (result > 0) { print("两物体面向"); } else { print("两物体背向"); } }
🟧 叉乘判断物体在自己左边还是右边(顺时针/逆时针)
橙哥之前看椿主播玩《小小梦魇》时,也太有意思了
但是自己却没时间玩 呜呜
1️⃣ 《小小梦魇》应用案例
在游戏里,玩家藏在了椅子下,怪物追过去,
那怪物是怎么判断出来,玩家在自己左侧还是右侧,从而控制向左转还是向右转,使动画更自然的呢?
有的小老弟就说了:
橙哥,咱们不能使用LookAt吗,或者Dotween的DoLookAt,
实在不行,自动寻路也行呀
首先啊,我们先来说下LookAt,
LookAt是瞬间让自己朝向目标,动作生硬不合适。
如果在Update里时刻朝向,那怪物可能会斜着走路。
DoLookAt确实能解决让怪物看向玩家,也挺好用的,
但是在游戏中,为了让动画自然,我们仍要知道该用向左转,还是向右转的动画,需要判断方位。
自动寻路,只能控制位置移动,不能控制旋转呢。
2️⃣ 推导逻辑
a X b = c , c⊥a,c⊥b。
叉乘得到是:一个与这两个向量都垂直的向量。
这个向量的方向满足右手螺旋法则:
a×b的方向:四指由a开始,指向b,拇指的指向就是a×b的方向,垂直于a和b所在的平面;
b×a的方向:四指由b开始,指向a,拇指的指向就是b×a的方向,垂直于b和a所在的平面;
a×b的方向与b×a的方向是相反的,所以:a×b=-b×a,负号代表的是方向相反。
向量都是有方向的,
比如我们的a、b两个向量(x1,y1,z1)(x2,y2,z2)
都在x、y平面,那这两个向量相乘,结果的x、y值肯定为正,只需判断Z值,根据正负即可知道右手螺旋法则大拇指朝上还是朝下
即可知道b在a的左侧还是右侧。
若都在x、z平面,咋判断y。同理,z、y平面,则判断x
3️⃣ Unity代码
请注意:
Unity当中使用左手,因为Unity使用的是左手坐标系
任意物体的坐标不应是原点,因为0x任何数都为0,判断不了方向。
该代码用来判断trans01在trans00的左边还是右边。
Vector3 result = Vector3.Cross(trans00.position, trans01.position); print(result);
先说结论:
使用叉乘判断方位,需要看在什么平面、以哪个轴为前方,才能判断。
不同的平面和不同的轴为前方,判断出来的正负不一样。
以下选中的物体为trans01,未选中的为trans00,
举了几个例子作为参考:
🚩 所以在X、Z平面,看Y值
当以Z轴为前方时,
当B在A右侧时,Y值>0
当B在A左侧时,Y值<0
🚩 在X、Y平面,看Z值
当以X轴为前方时,
当B在A右侧时,Y值>0
当B在A左侧时,Y值<0
🚩 在Y、Z平面,看X值
如果以Z轴方向为前方:
当B在A右侧时,X值<0
当B在A左侧时,X值>0
如果以Y轴方向为前方:
当B在A右侧时,X值>0
当B在A左侧时,X值<0
以都在X、Z平面,Z轴为前方来举例:
当B在A右侧时,Y值>0
当B在A左侧时,Y值<0
代码:
public Transform trans00; public Transform trans01; void Update() { Vector3 result = Vector3.Cross(trans00.position, trans01.position); print(result); if (result.y == 0) { print("trans01在trans00前方的这条线上"); } else if (result.y > 0) { print("trans01在右侧"); } else if (result.y < 0) { print("trans01在左侧"); } }
🟨 点乘判断角度
最后一个问题,怪物转身,应该转多少度呢?
其实这个点乘和叉乘都能判断角度。
因为:
点乘: a · b = |a|*|b| cosθ
叉乘: |aXb| = |a|*|b|*sinθ
但大家都用点乘来做,那我们也用点乘吧。
看到这是不是这累了,来波福利~
点乘计算的角度跟Vector.Angle结果一致,都是0-180度,用哪个都可以。
Unity公式:
public Transform trans00; public Transform trans01; void Update() { float cosAngle = Vector3.Dot(trans00.forward.normalized, trans01.forward.normalized); float angleDot = Mathf.Acos(cosAngle) * Mathf.Rad2Deg; float angleVector = Vector3.Angle(trans00.forward, trans01.forward); print(angleDot); print(angleVector); }