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

Unity纸牌开发:销毁实例化卡牌后重建报引用丢失异常如何解决?

纸牌游戏销毁卡牌时的MissingReferenceException问题

我正在开发一款纸牌游戏,遇到了功能实现问题:
玩家点击牌堆时,卡牌会生成到弃牌堆,卡牌通过工厂类创建,代码如下:

[SerializeField]private GameObject prefab;

public void Create(Transform parent, float zOffset, float yOffset, CardModel model, bool isCovered, bool isLast, string pileName, GameObject curretPile, ActionsManager manager)
{
    GameObject instance = Instantiate(prefab, new Vector3(parent.position.x, parent.position.y - yOffset, parent.position.z - zOffset), Quaternion.identity, parent);
    this.model = model;
    this.view = instance.GetComponent<CardView>();
    this.controller = GetComponent<CardController>();
    instance.GetComponent<CardController>().SetModel(this.model);
    instance.GetComponent<CardController>().SetView(view);

    string spriteName = (instance.GetComponent<CardController>().ReturnValue()).ToString() + instance.GetComponent<CardController>().ReturnSuit();
    Sprite faceingSprite = uncoverSprite.Find(item => item.name == spriteName);
    instance.GetComponent<CardController>().SetUncoveredSprite(faceingSprite);

    instance.GetComponent<CardController>().SetPileName(pileName);
    instance.GetComponent<CardController>().SetIsCoveredBool(isCovered);
    instance.GetComponent<CardController>().SetIsLastInPile(isLast);
    instance.GetComponent<CardController>().SetActionsManager(manager);
    instance.GetComponent<CardController>().SetCurrentPile(curretPile);
}

当所有卡牌都生成到弃牌堆后,玩家再次点击牌堆,会触发动作请求弃牌堆的卡牌列表,卡牌返回牌堆并在弃牌堆中销毁,销毁代码如下:

public void OnCardDemand()
{
    List<CardModel> list = ReturnList();
    RemoveAllElementsFromList();

    if(OnDeckSend != null)
    {
        OnDeckSend(list);
    }

    CardController[] cards = gameObject.GetComponentsInChildren<CardController>();

    foreach(CardController card in cards)
    {
        Destroy(card.gameObject);
    }
}

原本点击牌堆后卡牌应重新实例化,但出现错误:
MissingReferenceException: The object of type 'SpriteRenderer' has been destroyed but you are still trying to access it.


解决方案
  • 先清理引用再销毁
    给CardController添加清理方法,销毁前清空所有资源引用和事件订阅,避免后续代码访问已销毁对象:

    public void Cleanup()
    {
        // 清空SpriteRenderer的Sprite引用
        if (view != null && view.SpriteRenderer != null)
        {
            view.SpriteRenderer.sprite = null;
        }
        // 取消事件订阅(如果有)
        if (actionsManager != null)
        {
            // 示例:取消订阅ActionsManager的事件
            actionsManager.CardClicked -= OnCardClicked;
        }
        // 清空其他引用
        model = null;
        currentPile = null;
        actionsManager = null;
    }
    

    修改销毁逻辑,先调用清理再销毁:

    foreach(CardController card in cards)
    {
        card.Cleanup();
        Destroy(card.gameObject);
    }
    
  • 在访问组件前做有效性检查
    在所有可能访问SpriteRenderer的代码(比如CardView的Update、卡牌交互逻辑)开头,加入对象有效性判断:

    // 在CardView的Update或事件回调中
    if (this == null || spriteRenderer == null) return;
    

    也可以用TryGetComponent安全获取组件,替代直接访问:

    if (!TryGetComponent(out SpriteRenderer renderer)) return;
    // 使用renderer进行操作
    
  • 修复工厂类的引用错误
    看你的Create方法里,this.controller = GetComponent<CardController>();获取的是工厂自身的控制器,应该改为获取新实例的控制器:

    this.controller = instance.GetComponent<CardController>();
    

    避免工厂类持有已销毁卡牌的控制器引用,导致后续创建时触发错误。

  • 避免异步销毁后的引用访问
    Unity的Destroy是帧末异步执行的,如果销毁后有代码(比如下一帧的Update)还在访问该对象,就会报错。确保销毁后没有逻辑再引用这些卡牌对象,比如清空弃牌堆的卡牌列表时要彻底,不要残留旧引用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 00:35:19