问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Unity3D游戏中攻击判定的三种实现方式

创作时间:
作者:
@小白创作中心

Unity3D游戏中攻击判定的三种实现方式

引用
1
来源
1.
https://www.nowcoder.com/discuss/601045247090565120

在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 ) // 当逗留碰撞器

具体这两种碰撞什么时候可以检测到如图所示

碰撞检测的优点:

  • 碰撞体做的越精细越贴合模型,攻击判定就越真实,不会出现穿模的情况
  • 复杂度在模型上,代码工作量小

碰撞检测的缺点:

  • 三维的碰撞体参与计算性能开销较大,且碰撞体越精细开销就越大
  • 绘制不规则碰撞体需要额外的建模技能,美术工作量大
  • 由于发生碰撞的复杂条件,细节的偏差会产生意想不到的效果,使得必须制定规则设定不同图层间的碰撞逻辑,大大增加了工作量
  • 脚本调用需要满足“进入”/“离开条件”,在运动速度过快时可能会发生碰撞体穿透。常见的有FPS游戏中的子弹,在一帧内移动距离超过自身碰撞体的长度,就不会触发对应函数。解决办法有降速,加大碰撞体的长度,使用射线进行预判等,Unity官方也提供了一种解决方案——提高检测级别。
  • 将默认的离散检测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[] 扫描中命中的所有碰撞体的数组

射线检测的优点:

  • 表现效果好,性能开销一般
  • 适合多种需求,便于开发扩展

射线检测的缺点:

  • 代码工作量大,检测时会产生大量缓存,需要垃圾处理和优化

参考资料:

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号