Unity纸牌开发:销毁实例化卡牌后重建报引用丢失异常如何解决?
我正在开发一款纸牌游戏,遇到了功能实现问题:
玩家点击牌堆时,卡牌会生成到弃牌堆,卡牌通过工厂类创建,代码如下:
[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

