Unity中Raycast检测到敌人但大球爆炸时无法造成伤害问题排查
问题分析与解决方案
问题现象
玩家按住Q键生成球体,按住时间越长球体体积越大,出现如下异常:
- 小球爆炸后,可检测到爆炸半径内的敌人并对部分敌人造成伤害;
- 大球爆炸后,能检测到所有敌人,但完全无法造成伤害。
实现逻辑:通过OverlapSphereNonAlloc检测爆炸范围内的物体,再对每个物体发射Raycast验证遮挡并施加伤害。
核心问题定位
代码中存在多个逻辑错误,导致大球爆炸时伤害失效:
- Raycast方向计算错误:使用
Hits[i].transform.position - transform.position作为射线方向,而非从射线起点posRay到目标的方向,大球时子弹自身体积大,射线可能直接命中自身碰撞体,中断后续逻辑。 - 冗余无效的Raycast判断:第一个
Physics.Raycast仅判断是否有碰撞,未验证命中目标,且未限制检测层,可能提前触发无关碰撞。 - 射线起点偏移无意义:
posRay.transform.position + new Vector3(x, y, z)的偏移量未明确用途,大球时可能导致射线偏离目标,或误命中场景障碍物。 - Raycast未做范围与层级限制:未设置射线最大距离(应匹配爆炸半径),也未使用
BlockExplo层掩码过滤障碍物,大球时射线易误判遮挡。
修复后的代码
using UnityEngine; public class BULLET : MonoBehaviour { [SerializeField] float countDown; [SerializeField] int Maxhits = 25; bool destroyy = false; public float Radius = 10f; public float explosiveForce; [SerializeField] LayerMask BlockExplo; private Collider[] Hits; [SerializeField] GameObject posRay; [SerializeField] GameObject Ps_Explose; [SerializeField] float MaxDamage; [SerializeField] float MinDamage; private void Start() { Hits = new Collider[Maxhits]; } private void FixedUpdate() { countDown -= Time.deltaTime; if (destroyy || countDown <= 0) { int hits = Physics.OverlapSphereNonAlloc(posRay.transform.position, Radius, Hits); GameObject PS = Instantiate(Ps_Explose, transform.position, transform.rotation); PS.transform.localScale = transform.localScale * 1.5f; for (int i = 0; i < hits; i++) { // 跳过自身碰撞体,避免射线误判 if (Hits[i].gameObject == gameObject) continue; if (Hits[i].TryGetComponent<Rigidbody>(out Rigidbody rigidbody)) { Vector3 targetPos = Hits[i].transform.position; Vector3 rayStart = posRay.transform.position; float distance = Vector3.Distance(rayStart, targetPos); Vector3 rayDirection = (targetPos - rayStart).normalized; RaycastHit hit; // 规范射线参数:限制距离为爆炸半径,使用障碍物层掩码,忽略触发器 if (Physics.Raycast(rayStart, rayDirection, out hit, Radius, BlockExplo, QueryTriggerInteraction.Ignore)) { Debug.DrawLine(rayStart, hit.point, Color.red); // 确认命中目标为当前检测对象且未被墙遮挡 if (hit.transform.tag != "Walls" && hit.transform == Hits[i].transform) { rigidbody.AddExplosionForce(explosiveForce, rayStart, Radius); if (hit.transform.CompareTag("Enemy")) { int damage = Mathf.FloorToInt(Mathf.Lerp(MaxDamage, MinDamage, distance / Radius)); hit.transform.GetComponent<EnemyHp>().enemyHp -= damage; } } } } } Destroy(gameObject); } } private void OnTriggerEnter(Collider other) { if (other.CompareTag("Walls") || other.CompareTag("Enemy")) { destroyy = true; } } private void OnDrawGizmos() { // 绘制与实际检测范围一致的Gizmos Gizmos.DrawSphere(posRay.transform.position, Radius); } }
关键修复点说明
- 修正射线方向:使用
(targetPos - rayStart).normalized确保射线从posRay指向目标,避免方向偏移。 - 过滤自身碰撞体:跳过子弹自身的碰撞体,防止射线误判。
- 规范Raycast参数:设置最大距离为爆炸半径,使用
BlockExplo层掩码过滤障碍物,忽略触发器。 - 验证命中一致性:确保射线命中的对象就是
OverlapSphere检测到的目标,避免中间遮挡导致的误判。 - 移除冗余逻辑:删除无意义的前置Raycast判断,简化代码逻辑。
- 同步Gizmos显示:改为绘制
posRay位置的爆炸半径,与实际检测范围一致。
内容的提问来源于stack exchange,提问作者Giorgos Pro
相关产品推荐
相关产品推荐

