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

Unity中FindGameObjectsWithTag仍找到已销毁对象的问题及解决方法

Unity销毁对象后仍被FindGameObjectsWithTag找到导致MissingReferenceException的原因与解决办法

原因

  • Unity的Destroy()方法并非立即销毁对象,只是将其标记为待销毁状态,真正的销毁操作会延迟到当前帧的Update阶段结束后、LateUpdate之后的专属销毁阶段执行。因此同一帧内的其他Update方法调用FindGameObjectsWithTag时,依然会检索到这个已标记但未真正移除的对象。
  • 被标记销毁的对象引用不会直接变为null,而是成为一个“无效引用”,当代码尝试访问该对象的属性、方法或组件时,就会触发MissingReferenceException。

解决办法

1. 避免同一帧内的依赖冲突

  • 调整逻辑执行时机:将销毁对象的逻辑移到EarlyUpdate中执行,确保在其他Update方法执行前完成标记;或者让查找逻辑延迟到下一帧(比如用Invoke延迟0秒,或者在LateUpdate中执行查找)。
  • 维护自定义对象列表:提前将需要管理的目标对象存入List<GameObject>或HashSet<GameObject>,销毁时直接从列表中移除,后续业务逻辑直接遍历这个自定义列表,不再依赖FindGameObjectsWithTag这类全局查找接口。

2. 检查对象有效性

每次通过查找获取对象引用后,先做有效性校验,避免访问无效对象:

GameObject[] objects = GameObject.FindGameObjectsWithTag("TargetTag");
foreach (var obj in objects)
{
    // 基础校验:Unity对已销毁对象重载了==运算符,会返回false
    if (obj == null) continue;
    
    // 更严谨的校验方式(避免自定义==重载干扰)
    if (Object.ReferenceEquals(obj, null)) continue;
    
    // 或者通过尝试获取组件间接判断
    if (!obj.TryGetComponent<Transform>(out _)) continue;
    
    // 执行后续逻辑
}

3. 用对象池替代销毁

如果目标对象需要频繁创建和销毁,改用对象池模式:

  • 不调用Destroy(),而是将对象设置为非激活状态(obj.SetActive(false)),此时FindGameObjectsWithTag不会返回非激活对象。
  • 需要复用对象时,从对象池中取出并重新激活(SetActive(true)),既避免了销毁重建的性能开销,也从根源上消除了无效引用问题。

4. 自定义状态标记延迟销毁

给目标对象添加自定义状态字段,标记其是否待销毁:

public class TargetObject : MonoBehaviour
{
    public bool isMarkedForDestroy = false;
}

销毁前先标记状态,延迟一帧再执行销毁:

// 销毁逻辑
var target = GetComponent<TargetObject>();
target.isMarkedForDestroy = true;
Destroy(target.gameObject, 0); // 延迟0秒会在下一帧执行销毁

其他方法查找后先检查状态:

foreach (var obj in GameObject.FindGameObjectsWithTag("TargetTag"))
{
    var targetComp = obj.GetComponent<TargetObject>();
    if (targetComp != null && targetComp.isMarkedForDestroy) continue;
    
    // 执行后续逻辑
}

内容的提问来源于stack exchange,提问作者Sho Boi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 09:54:24