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

Unity中Physics.OverlapSphere访问已销毁对象问题排查

爆炸桶连锁爆炸时访问已销毁对象的错误原因及修复

问题原因

  1. Unity对象销毁机制延迟:Destroy(gameObject)不会立刻从内存和物理系统中移除对象,只是标记为待销毁,要等到当前帧所有逻辑执行完毕后才会真正清理。当爆炸桶A触发爆炸并调用Destroy后,同一帧内被触发的爆炸桶B执行Physics.OverlapSphere时,仍然能检测到A桶的Collider,但此时A桶的组件已经处于失效状态,后续访问就会抛出错误。
  2. 空值判断顺序错误:代码中hit == null的判断放在了hit.GetComponent<explosiveBarrel>()之后,而如果hit对应的对象已被标记销毁,调用GetComponent时就会先触发空引用错误,根本走不到null判断的分支。
  3. 状态标记无法拦截物理检测:alreadyExploded标记只能阻止重复触发爆炸逻辑,但无法让物理系统忽略已标记销毁的对象,OverlapSphere仍然会把它纳入检测结果。

修复方案

1. 提前校验Collider有效性

在处理每个检测到的Collider时,优先判断对象是否有效,避免后续无效调用:

foreach (Collider hit in colliders)
{
    // 优先判断对象是否已销毁或禁用
    if (hit == null || !hit.gameObject.activeInHierarchy)
    {
        Debug.Log("Object doesn't exist anymore or is disabled");
        continue;
    }

    Rigidbody exploRB = hit.GetComponent<Rigidbody>();
    if (exploRB == null)
    {
        Debug.Log("no rigidbody - cringe");
        continue;
    }

    explosiveBarrel barrel = hit.GetComponent<explosiveBarrel>();
    if (barrel != null)
    {
        if (barrel.alreadyExploded)
        {
            Debug.Log("Exploding barrel has already been made to explode");
            exploRB.AddExplosionForce(explosionForce, transform.position, explosionRadius, 1f, ForceMode.Impulse);
        }
        else
        {
            Debug.Log("ooo an existing barrel - ill save you later hehe uwu");
            existingBarrels.Add(hit.gameObject);
            exploRB.AddExplosionForce(explosionForce, transform.position, explosionRadius, 1f, ForceMode.Impulse);
        }
    }
    else
    {
        exploRB.AddExplosionForce(explosionForce, transform.position, explosionRadius, 1f, ForceMode.Impulse);
        Debug.Log("I moved a non exploding object");
    }
}

2. 先禁用对象再延迟销毁

替换直接销毁为先禁用对象,让物理系统立即忽略该对象的Collider,再延迟销毁以保证特效播放完成:

ps.Play();
// 立即禁用对象,物理系统会停止检测该对象
gameObject.SetActive(false);
// 根据粒子特效时长延迟销毁
Destroy(gameObject, ps.main.duration);
Debug.Log("explode pog");
yield return null;

3. 优化物理检测层

给爆炸桶和需要检测的对象单独设置物理层,调用Physics.OverlapSphere时指定只检测目标层,减少无效检测:

// 提前定义层掩码,比如爆炸桶层和可受爆炸影响的物体层
int layerMask = LayerMask.GetMask("ExplosiveBarrel", "Destructible");
colliders = Physics.OverlapSphere(transform.position, explosionRadius, layerMask);

内容的提问来源于stack exchange,提问作者Launcelot Raven

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 23:30:27