You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

Unity中如何正确取消GameManager事件订阅?解决场景清理报错

解决Unity委托订阅销毁顺序导致的场景清理警告

我之前也踩过这个Unity生命周期的坑!这个报错的核心原因是场景销毁时对象的销毁顺序不确定——你的GameManager实例比Door更早被销毁,当Door在OnDisableOnDestroy里尝试取消订阅委托时,GameManager的委托已经随着对象销毁而失效,这就触发了残留引用的警告。

下面是几个经过验证的解决方案,按推荐程度排序:

方案1:用DontDestroyOnLoad让GameManager全局存活

如果你的GameManager是全局单例(大多数情况都是),在它的Awake方法里加一行代码,让它不受场景销毁影响:

private void Awake()
{
    DontDestroyOnLoad(gameObject);
    // 单例初始化逻辑...
}

这样GameManager会成为全局持久化对象,在游戏停止时才会最后被销毁,确保Door的取消订阅逻辑执行时,GameManager还存在,不会出现空引用问题。

方案2:取消订阅前强制做空值检查

如果不想让GameManager全局存活,那就在Door的取消订阅代码里加上严格的空值判断,避免在GameManager已经销毁时操作委托:

private void OnDisable()
{
    // 假设GameManager用单例模式通过Instance访问
    if (GameManager.Instance != null && GameManager.Instance.MyDelegate != null)
    {
        GameManager.Instance.MyDelegate -= OnDelegateTriggered;
    }
}

空值检查能直接跳过无效的取消订阅操作,消除警告。

方案3:替换成Unity内置的UnityEvent

Unity自带的UnityEvent已经帮我们处理了对象销毁后的引用问题——它内部维护的是弱引用,当订阅者(比如Door)销毁时会自动移除监听,完全不需要手动取消订阅。用法示例:

在GameManager里定义事件:

using UnityEngine.Events;

public class GameManager : MonoBehaviour
{
    public UnityEvent MyGameEvent;
    // 其他逻辑...
}

在Door里订阅:

private void OnEnable()
{
    GameManager.Instance.MyGameEvent.AddListener(OnGameEventTriggered);
}

// 不需要在OnDisable/OnDestroy里写移除代码!

这种方式从根源上避免了销毁顺序的问题,是Unity官方推荐的事件实现方式。

方案4:手动调整脚本执行顺序

如果以上方法都不适用,你可以手动控制销毁顺序:

  1. 打开Edit > Project Settings > Script Execution Order
  2. 把GameManager的执行顺序设为更小的数值(比如-100),Door设为更大的数值(比如0)
  3. 应用设置

Unity的销毁顺序和Awake执行顺序相反:Awake先执行的对象会更晚被销毁。这样调整后,GameManager会比Door晚销毁,Door执行OnDestroy时GameManager还在,就能正常取消订阅。


内容的提问来源于stack exchange,提问作者Sean Carey

火山引擎 最新活动