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

Unity销毁GameObject后仍可访问问题及检测方法咨询

问题描述

我有两个Unity GameObject:monster(怪物)和hunter(猎人),各自挂载一个脚本。从怪物脚本中调用猎人脚本的函数销毁猎人对象,但猎人对象并未被置空。猎人实体已从屏幕消失,却仍能访问该对象及其成员。

HunterScript 代码

public class HunterScript : MovingEntityScript { // 最终继承自 MonoBehaviour

    int hp = 2;

    // 其他代码

    public void TakeDamage () {
        if (--hp == 0) Destroy(gameObject);
    }

}

MonsterScript 代码

public class MonsterScript: MovingEntityScript { // 最终继承自 MonoBehaviour

    GameObject target; // 启动时设置为唯一的 Hunter 实例

    // 其他代码

    public void Attack () {
        target.GetComponent<HunterScript>().TakeDamage();
        Debug.Log(target == null || target.Equals(null)); // 即使猎人 hp == 0,也打印 False
    }

}

怪物和猎人都大量使用协程,包括猎人销毁时也有协程。请问为何会出现这种情况?如何检测实体是否已真正被销毁?


原因分析
  • Destroy是延迟执行的:Unity的Destroy()不会立刻销毁对象,只是给它标记为「待销毁」,实际销毁操作要等当前帧Update循环结束、渲染开始前,或者下一帧才会执行。所以调用Destroy()的同一帧里,target仍然指向原对象实例,不会立刻变为null。
  • C#对象与Unity引擎实体分离:GameObject是C#层的包装类,背后对应Unity引擎底层的实体。调用Destroy()后,引擎实体被标记销毁,但C#的对象实例不会马上被垃圾回收,所以你仍能访问它的成员变量,直到GC清理掉这个C#对象。
  • 协程延长对象生命周期:如果猎人销毁时还有未完成的协程,这些协程会继续执行到结束(除非手动停止),这会拖长C#对象的生命周期,导致它更晚被回收,自然就能一直访问到。
正确检测对象是否销毁的方法
  • 延迟一帧用target == null判断:Unity重载了GameObject的==运算符,会同时检查引擎实体是否被销毁,但必须等Destroy()执行后的下一帧,这个判断才会返回true。可以用协程实现延迟:
    public void Attack () {
        target.GetComponent<HunterScript>().TakeDamage();
        StartCoroutine(CheckTargetStatusNextFrame());
    }
    
    IEnumerator CheckTargetStatusNextFrame() {
        yield return null;
        Debug.Log(target == null); // 此处会打印true
    }
    
  • 监听OnDestroy事件主动置空:在猎人脚本中添加销毁事件,让怪物脚本订阅后主动把target置空:
    猎人脚本补充代码:
    public event Action OnHunterDestroyed;
    
    void OnDestroy() {
        OnHunterDestroyed?.Invoke();
    }
    
    怪物脚本初始化时订阅:
    void Start() {
        var hunterComp = target.GetComponent<HunterScript>();
        hunterComp.OnHunterDestroyed += () => target = null;
    }
    
  • 用TryGetComponent前置检查:访问组件前先尝试获取,失败则说明对象已销毁或组件不存在:
    public void Attack () {
        if (target.TryGetComponent(out HunterScript hunter)) {
            hunter.TakeDamage();
        } else {
            Debug.Log("猎人已销毁");
        }
    }
    
  • 检查activeInHierarchy属性:对象销毁后activeInHierarchy会返回false,但要注意如果只是用SetActive(false)禁用对象,该值也会是false,需要结合场景逻辑区分。

内容的提问来源于stack exchange,提问作者An Assembler

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 02:55:25