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

Unity Fishnet OnStopClient偶发MissingReferenceException问题求助

问题描述

使用Fishnet的OnStartClient和OnStopClient方法时遇到异常:

  • 启动客户端时通过GameObject.FindGameObjectWithTag("InventoryPanel")获取对应GameObject并赋值给invPanel
  • 停止客户端时访问invPanel,尝试判断其激活状态并设置为激活
  • 测试发现:每次启停客户端3次,前两次触发MissingReferenceException(提示对象已销毁仍被访问),第三次却能正常运行,不符合预期的“始终正常/始终异常”结果

相关代码片段:

public override void OnStopClient()
{
    base.OnStopClient();
    
    // 此处有时会抛出空引用异常,前两次启停报错,第三次正常
    // invPanel是在OnStartClient中通过GameObject.FindGameObjectWithTag("InventoryPanel")获取的
    if (!invPanel.activeSelf)
    {
        invPanel.SetActive(true);
    }
}
原因分析与解决方案

核心原因

  1. 僵尸引用残留:Unity中已销毁的对象不会自动将变量置为null,invPanel可能持有已销毁对象的无效引用,此时访问其属性就会触发异常
  2. 生命周期顺序差异:前两次启停时,OnStopClient的执行时机晚于InventoryPanel的销毁逻辑;第三次可能因对象复用、场景加载缓存等原因,InventoryPanel尚未被销毁,或重新获取到了有效引用

修复步骤

方案1:访问前严格检查引用有效性

Unity重载了==运算符,可直接判断对象是否已销毁或为空:

public override void OnStopClient()
{
    base.OnStopClient();
    
    // 先判断引用是否有效
    if (invPanel == null)
    {
        // 尝试重新获取引用,避免缓存的引用失效
        invPanel = GameObject.FindGameObjectWithTag("InventoryPanel");
        // 仍找不到则直接返回
        if (invPanel == null) return;
    }
    
    if (!invPanel.activeSelf)
    {
        invPanel.SetActive(true);
    }
}

方案2:每次使用时重新查找(更稳妥)

放弃缓存引用,在OnStopClient中直接查找目标对象,彻底避免过期引用问题:

public override void OnStopClient()
{
    base.OnStopClient();
    
    GameObject invPanel = GameObject.FindGameObjectWithTag("InventoryPanel");
    if (invPanel != null && !invPanel.activeSelf)
    {
        invPanel.SetActive(true);
    }
}

额外优化建议

  • 避免频繁使用GameObject.FindGameObjectWithTag,可在客户端启动时将InventoryPanel注册到单例管理类,后续直接从单例获取,提升效率和可靠性
  • 检查InventoryPanel的销毁逻辑,确认是否在客户端停止时被不必要销毁;若需保留该对象,可添加DontDestroyOnLoad标记

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 18:32:34