You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中Raycast检测到敌人但大球爆炸时无法造成伤害问题排查

问题分析与解决方案

问题现象

玩家按住Q键生成球体,按住时间越长球体体积越大,出现如下异常:

  • 小球爆炸后,可检测到爆炸半径内的敌人并对部分敌人造成伤害;
  • 大球爆炸后,能检测到所有敌人,但完全无法造成伤害。

实现逻辑:通过OverlapSphereNonAlloc检测爆炸范围内的物体,再对每个物体发射Raycast验证遮挡并施加伤害。


核心问题定位

代码中存在多个逻辑错误,导致大球爆炸时伤害失效:

  1. Raycast方向计算错误:使用Hits[i].transform.position - transform.position作为射线方向,而非从射线起点posRay到目标的方向,大球时子弹自身体积大,射线可能直接命中自身碰撞体,中断后续逻辑。
  2. 冗余无效的Raycast判断:第一个Physics.Raycast仅判断是否有碰撞,未验证命中目标,且未限制检测层,可能提前触发无关碰撞。
  3. 射线起点偏移无意义:posRay.transform.position + new Vector3(x, y, z)的偏移量未明确用途,大球时可能导致射线偏离目标,或误命中场景障碍物。
  4. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 22:45:54