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

Unity TCG卡牌拖拽后无法返回原父物体问题求助

卡牌拖拽后无法返回原父物体问题排查与修复

问题描述

开发TCG集换式卡牌游戏时,卡牌拖拽功能其他逻辑正常,但释放卡牌后,卡牌未返回player1Hand区域,而是回到主屏幕根节点。C#代码无编译错误,但Unity控制台抛出NullReferenceException,提示在CardController.OnPointerUp方法中尝试访问空引用。

控制台错误信息

NullReferenceException: Object reference not set to an instance of an object
CardController.OnPointerUp(PointerEventData eventData) (at Assets/Scripts/CardController.cs:XX)


优化后的代码

CardController 卡牌交互控制器

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using TMPro;

// 处理卡牌的拖拽、点击交互逻辑
public class CardController : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IDragHandler
{
    // 当前卡牌绑定的数据对象
    public Card card;
    // 显示卡牌角色精灵的Image组件
    public Image characterSprite;
    // 卡牌背景Image组件(控制射线检测)
    private Image _cardBackground;
    // 卡牌属性文本组件
    public TextMeshProUGUI cardNameText, attackText, defenceText, levelText, descriptionText;
    // 存储卡牌初始父物体,用于拖拽结束后返回原位置
    private Transform _originalParent;

    // 初始化组件引用
    private void Awake()
    {
        _cardBackground = GetComponent<Image>();
    }

    // 初始化卡牌数据与UI显示
    public void Initialize(Card cardData)
    {
        card = cardData;
        // 赋值角色精灵
        characterSprite.sprite = cardData.character;
        // 填充卡牌属性文本
        cardNameText.text = cardData.cardName;
        attackText.text = cardData.attack.ToString();
        defenceText.text = cardData.defence.ToString();
        levelText.text = cardData.level.ToString();
        descriptionText.text = cardData.description;
        // 记录当前父物体为初始父物体
        _originalParent = transform.parent;
    }

    // 鼠标释放卡牌时触发的逻辑
    public void OnPointerUp(PointerEventData eventData)
    {
        // 恢复射线检测,允许卡牌再次被交互
        _cardBackground.raycastTarget = true;
        
        // 避免空引用错误:先检查初始父物体是否有效
        if (_originalParent != null)
        {
            // 将卡牌放回初始父物体
            transform.SetParent(_originalParent);
            // 重置卡牌在父物体内的位置为居中
            transform.localPosition = Vector3.zero;
        }
    }

    // 鼠标按下卡牌时触发的逻辑
    public void OnPointerDown(PointerEventData eventData)
    {
        // 将卡牌移至根节点,解决拖拽时的层级遮挡问题
        transform.SetParent(transform.root);
        // 禁用射线检测,防止拖拽过程中重复触发点击事件
        _cardBackground.raycastTarget = false;
    }

    // 拖拽过程中触发的逻辑
    public void OnDrag(PointerEventData eventData)
    {
        // 让卡牌跟随鼠标位置移动
        transform.position = eventData.position;
    }
}

CardManager 卡牌管理器

using UnityEngine;
using System.Collections.Generic;

// 全局卡牌管理类,负责卡牌生成与实例管理
public class CardManager : MonoBehaviour
{
    // 单例实例,供其他脚本全局调用
    public static CardManager Instance;
    // 存储所有可生成的卡牌数据列表
    public List<Card> cardDataList = new List<Card>();
    // 玩家1手牌区的父物体Transform
    public Transform player1HandArea;
    // 玩家2手牌区的父物体Transform
    public Transform player2HandArea;
    // 卡牌预制体(带CardController组件)
    public CardController cardPrefab;

    // 初始化单例
    private void Awake()
    {
        Instance = this;
    }

    // 游戏启动时生成初始手牌
    private void Start()
    {
        GenerateInitialHandCards();
    }

    // 遍历卡牌数据列表,生成卡牌并添加到玩家1手牌区
    private void GenerateInitialHandCards()
    {
        foreach (Card cardData in cardDataList)
        {
            // 实例化卡牌预制体,设置父物体为玩家1手牌区
            CardController newCard = Instantiate(cardPrefab, player1HandArea);
            // 重置卡牌在父物体内的位置为居中
            newCard.transform.localPosition = Vector3.zero;
            // 初始化卡牌数据与UI显示
            newCard.Initialize(cardData);
        }
    }
}

问题原因与修复说明

  1. 空引用错误根源:原代码中未对originalParent做空值检查,若该变量因意外情况(如预制体初始化顺序问题)未被正确赋值,会导致OnPointerUp时触发NullReferenceException,卡牌无法返回原父物体。
  2. 修复措施:
    • 在OnPointerUp中增加_originalParent != null的判断,确保只有当引用有效时才执行父物体设置。
    • 优化变量命名,避免与Unity内置组件名冲突(如将image改为_cardBackground),提升代码可读性与维护性。
    • 规范注释,明确每个组件、方法的作用,降低后续开发的理解成本。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 19:48:44