关于Unity3D游戏中攻击判定的一些思考
1. 基于碰撞检测
这是最容易想到的做法,利用Unity引擎自带的碰撞器组件,为攻击者和受击者添加碰撞器,在碰撞器的回调函数中处理想要实现的功能。在Unity中发生碰撞的物体分为两种:
- 发起碰撞的物体:Rigidbody,CharacterController
- 接受碰撞的物体:所有Collider
所有的Collider上都会带有一个isTrigger开关,当碰撞双方其中一个勾选时视为触发器,不参与后续物理反应,调用碰撞双方的onTrigger脚本
MonoBehaviour.OnTriggerEnter( Collider other ) //当进入触发器 MonoBehaviour.OnTriggerExit( Collider other ) //当退出触发器 MonoBehaviour.OnTriggerStay( Collider other ) //当逗留触发器
当双方均不勾选isTrigger时,调用碰撞双方的onCollision脚本,此时发起碰撞的物体的Rigidbody组件不能勾选isKinematic
MonoBehaviour.OnCollisionEnter( Collision collisionInfo ) //当进入碰撞器 MonoBehaviour.OnCollisionExit( Collision collisionInfo ) //当退出碰撞器 MonoBehaviour.OnCollisionStay( Collision collisionInfo ) // 当逗留碰撞器
具体这两种碰撞什么时候可以检测到如图所示
碰撞检测的优点:
碰撞检测的缺点:
将默认的离散检测Discrete改为连续检测Continuous,当然这又加大了性能的开销。
2. 基于向量计算
向量有方向也有长度,使用若干向量就可以绘制出攻击范围,遍历攻击范围内的所有目标就可以触发攻击判定了。
Physics.OverlapSphere(Vector3 position, float radius) //返回position为球心,radius为半径的球体空间 //内所有带有碰撞体的物体 Vector3.Dot(Vector A, Vector B) //返回两个物体间的夹角 Vector3.Distance(Vector positionA, Vector positionB)//返回两个物体间的距离
为了配合攻击动画,通常采用帧事件实现
在攻击动画的某些关键帧执行函数,遍历空间删选出处在攻击范围内的目标,触发攻击判定。向量计算的优点:
- 性能开销小
- 没有复杂的条件和逻辑,代码简单
向量计算的缺点:
- 当无用的小物体很多时需要考虑遍历的优化
- 当目标体积过大时,可能出现一小部分进入攻击范围导致误判,视觉效果不真实
- 较多用于范围类技能、爆炸物等的攻击判定,对于单体攻击类的判定效果不好
3. 基于射线检测
射线检测可以看作是对碰撞检测和向量计算的一个折中,在Unity中,射线可以设置发射点、长度、方向,返回射线穿透的物体。
public static bool Raycast(Vector3 origin,Vector3 direction,RaycastHit,float distance,int layerMask)
使用射线可以解决FPS游戏中子弹速度过快导致的碰撞体穿透问题,且性能开销更小,而对于较为复杂的攻击方式,比如刀剑的挥舞,一条射线就不能解决问题了。
public static bool Linecast (Vector3 start,Vector3 end,int layerMask= DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction=QueryTriggerInteraction.UseGlobal) //如果有任何碰撞体与 start 和 end 之间的线相交,则返回 true
public static int RaycastNonAlloc (Ray ray, RaycastHit[] results, float maxDistance= Mathf.Infinity, int layerMask= DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction= QueryTriggerInteraction.UseGlobal); //返回int 存储到 results 缓冲区的命中对象数量
在刀剑上记录若干点,攻击时,保存射线点这一帧的位置,然后在下一帧时,从上一帧发出射线到当前这一帧,检测连成的线段是否触碰目标物体,效果如下图所示(红线代表攻击动作每一帧射线点的连线,蓝线代表不同帧射线轨迹)。
此时构造的攻击平面的二维的,如果对攻击判定的厚度有需求,则可以使用球体投射碰撞检测,每帧以每个射线点为球心投射球体,形成的判定轨迹就是三维空间。
public static int SphereCastNonAlloc (Vector3 origin, float radius, Vector3 direction, RaycastHit[] results, float maxDistance= Mathf.Infinity, int layerMask= DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction= QueryTriggerInteraction.UseGlobal); //返回RaycastHit[] 扫描中命中的所有碰撞体的数组
射线检测的优点:
- 表现效果好,性能开销一般
- 适合多种需求,便于开发扩展
射线检测的缺点:
- 代码工作量大,检测时会产生大量缓存,需要垃圾处理和优化
参考资料:https://segmentfault.com/a/1190000012636643/
https://blog.csdn.net/cycler_725/article/details/119485577
https://blog.csdn.net/wch3351028/article/details/122326021
https://github.com/biangeqian/Graduation-project/tree/master/Scripts