Unity卡牌游戏:如何识别卡牌所属区域并实现跨区域位置调整
卡牌区域识别与拖拽移区实现方案
现有代码的核心问题
- 拖拽过程中临时将卡牌父节点设为Canvas,导致无法通过
transform.parent稳定识别卡牌所属区域 - 仅写死了BottomArea的碰撞检测,无通用全区域支持逻辑
- 针对UGUI元素使用2D物理碰撞检测的方案兼容性差,需要额外配置碰撞体、刚体,维护成本高
实现思路
1. 区域识别核心逻辑
为每个游戏区域配置专属Tag,卡牌上维护独立的currentArea变量缓存所属区域,仅在卡牌最终放置时更新该值,不受拖拽过程中临时父节点变化的影响。
所有区域识别、拖拽交互全部基于UGUI原生事件系统实现,不需要额外物理组件。
2. 代码实现
替换原卡牌拖拽脚本(原Click.cs)
using UnityEngine; using UnityEngine.EventSystems; public class CardDrag : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler { [Header("可允许移动的区域Tag列表")] public string[] validAreaTags = { "MyArea", "BottomArea" }; private GameObject currentArea; // 缓存卡牌当前实际所属区域 private GameObject hoverArea; // 缓存鼠标当前悬停的目标区域 private Transform originalParent; // 拖拽前的父节点 private Vector2 originalAnchoredPos; // 拖拽前的本地坐标 private Canvas rootCanvas; private RectTransform cardRect; private void Awake() { cardRect = GetComponent<RectTransform>(); rootCanvas = GetComponentInParent<Canvas>(); } public void OnBeginDrag(PointerEventData eventData) { currentArea = transform.parent.gameObject; // 校验当前卡牌是否在可拖拽的源区域内 bool canDrag = false; foreach (var tag in validAreaTags) { if (currentArea.CompareTag(tag)) { canDrag = true; break; } } if (!canDrag) return; // 缓存拖拽前的状态 originalParent = transform.parent; originalAnchoredPos = cardRect.anchoredPosition; // 临时置顶到Canvas顶层,避免被其他区域遮挡 transform.SetParent(rootCanvas.transform, false); GetComponent<CanvasGroup>().blocksRaycasts = false; } public void OnDrag(PointerEventData eventData) { cardRect.anchoredPosition += eventData.delta; // 实时检测鼠标当前悬停的区域 if (EventSystem.current.IsPointerOverGameObject()) { var hoverObj = EventSystem.current.currentSelectedGameObject; if (hoverObj != null && hoverObj.GetComponent<GameArea>() != null) { hoverArea = hoverObj; } } } public void OnEndDrag(PointerEventData eventData) { GetComponent<CanvasGroup>().blocksRaycasts = true; // 校验目标区域是否有效 bool validTarget = false; if (hoverArea != null) { foreach (var tag in validAreaTags) { if (hoverArea.CompareTag(tag)) { validTarget = true; break; } } } if (validTarget) { // 成功移动到目标区域,更新所属区域缓存 currentArea = hoverArea; transform.SetParent(hoverArea.transform, false); // 触发区域布局自动重算,比如使用HorizontalLayoutGroup排序卡牌 LayoutRebuilder.ForceRebuildLayoutImmediate(hoverArea.GetComponent<RectTransform>()); } else { // 未放置到有效区域,回到原状态 transform.SetParent(originalParent, false); cardRect.anchoredPosition = originalAnchoredPos; } hoverArea = null; } // 对外暴露获取卡牌所属区域的接口 public GameObject GetCurrentArea() { return currentArea; } }
新增游戏区域脚本(挂载在MyArea/LeftArea/RightArea/BottomArea四个物体上)
using UnityEngine; using UnityEngine.EventSystems; // 仅用于标识该物体是可放置卡牌的游戏区域 public class GameArea : MonoBehaviour { }
原有DrawCards脚本适配
原有实例化逻辑不需要大改,仅需做两个配置:
- 在Unity编辑器中给4个游戏区域设置对应Tag:
MyArea、LeftArea、RightArea、BottomArea - 给所有卡牌预设体添加
CanvasGroup组件,保持blocksRaycasts默认勾选即可
补充说明
- 任意卡牌的所属区域可以通过
cardObject.GetComponent<CardDrag>().GetCurrentArea()直接获取,不需要遍历父节点 - 要调整允许的拖拽转移规则,只需要修改
validAreaTags数组的值,不需要改动核心拖拽逻辑
注意:要确保所有区域和卡牌都在同一Canvas下,且场景中存在
EventSystem物体,否则事件接口不会触发。
内容的提问来源于stack exchange,提问作者Jemima Boodhun
相关产品推荐
相关产品推荐

