物体拖拽效果OnDrag

本文介绍了一个Unity游戏开发中的UI物品拖拽系统实现方案。该方案利用MonoBehaviour组件和Event System接口来处理UI元素的拖拽操作,并通过射线检测确定物品放置位置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[csharp]  view plain  copy
 print ?
  1. using UnityEngine;  
  2. using System.Collections;  
  3. using UnityEngine.UI;  
  4. using UnityEngine.EventSystems;  
  5.   
  6. public class PetItemMove : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, ICanvasRaycastFilter  
  7. {  
  8.     private static Transform canvasTra;  
  9.     private Transform nowParent;//物品是格子的子物体,nowParent记录的是当前物品属于哪个格子(格子1)  
  10.     private bool isRaycastLocationValid = true;//默认射线不能穿透物品  
  11.     private Camera canvasCamera = null;  
  12.     void Start()  
  13.     {  
  14.   
  15.     }  
  16.   
  17.     public void OnBeginDrag(PointerEventData eventData)//开始拖拽  
  18.     {  
  19.         if (canvasTra == null) canvasTra = GameObject.Find("Canvas").transform;  
  20.         canvasCamera = canvasTra.GetComponentInChildren<Camera>();  //UICamera
  21.         //Debug.Log(Input.mousePosition);  
  22.         nowParent = transform.parent;//nowparent为被拖拽物体的原始父物体  
  23.         transform.SetParent(canvasTra);//将当前拖拽的物品放在canvas下  
  24.         isRaycastLocationValid = false;//ui穿透:置为可以穿透  【拖拽物体移动的时候鼠标下是有物体一直跟随遮挡的,如果不穿透就获取不到放置位置(OnEndDrag中判断是空格子,物体,还是无效位置)】  
  25.   
  26.     }  
  27.   
  28.     public void OnDrag(PointerEventData eventData)//拖拽过程中  
  29.     {  
  30.        transform.position = canvasCamera.ScreenToWorldPoint(Input.mousePosition);//鼠标左键按住拖拽的时候,物体跟着鼠标移动  
  31.         //transform.position = Input.mousePosition;//鼠标左键按住拖拽物体的时候不显示物体  
  32.   
  33.     }  
  34.   
  35.     public void OnEndDrag(PointerEventData eventData)//  
  36.     {  
  37.         GameObject go = eventData.pointerCurrentRaycast.gameObject;//获取到鼠标终点位置下 可能的物体  
  38.   
  39.           
  40.         if (go != null)  
  41.         {  
  42.             Debug.Log(go.name);  
  43.             if (go.tag.Equals("Grid"))//鼠标终点位置下是: 空格子(所以直接放进去)  
  44.             {  
  45.                 SetParentAndPosition(transform, go.transform);  
  46.             }  
  47.             else if (go.tag.Equals("PlayerPetItem"))//鼠标终点位置下是: 也是一个物体(所以需要交换位置)  
  48.             {//交换位置要注意可能需要把物品下的子物体的Raycast Target关掉(不去掉可能无法交换)  
  49.                 SetParentAndPosition(transform, go.transform.parent);//将被拖拽的物体1放到鼠标终点下的格子2里面  
  50.                 SetParentAndPosition(go.transform, nowParent);//将鼠标终点格子2里面物体2 放到 原来物体1的格子1里面  
  51.                 if (transform.position == go.transform.position)  
  52.                 {  
  53.                     Debug.Log("error");  
  54.                 }  
  55.             }  
  56.             else//鼠标终点是:无效位置(所以物体放回原来的位置)  
  57.             {  
  58.                 SetParentAndPosition(transform, nowParent);  
  59.             }  
  60.         }  
  61.         else//  
  62.         {  
  63.             SetParentAndPosition(transform, nowParent);  
  64.         }  
  65.         isRaycastLocationValid = true;//ui事件穿透:置为不能穿透  
  66.     }  
  67.   
  68.     private void SetParentAndPosition(Transform child, Transform parent)//将child放到parent下作为子物体  
  69.     {  
  70.         child.SetParent(parent);  
  71.         child.position = parent.position;  
  72.     }  
  73.   
  74.     public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)//UI事件穿透:如置为false即可以穿透,被图片覆盖的按钮可以被点击到  
  75.     {  
  76.         return isRaycastLocationValid;  
  77.     }  
  78. }  
 
### Unity3D 中实现物体拖拽功能 在 Unity3D 中实现物体拖拽功能可以通过多种方式完成,具体取决于项目需求以及目标平台。以下是基于提供的引用内容和专业知识整理的一个完整的解决方案。 #### 1. 使用事件系统接口 (IBeginDragHandler, IDragHandler, IEndDragHandler) 为了实现拖拽效果,可以利用 Unity 的 `EventSystem` 提供的接口: - **开始拖拽**:通过实现 `IBeginDragHandler` 接口,在用户点击并准备拖动物体时触发逻辑。 - **持续拖拽**:通过实现 `IDragHandler` 接口,在用户移动手指或鼠标的过程中实时更新物体位置。 - **结束拖拽**:通过实现 `IEndDragHandler` 接口,在用户释放物体时执行清理或其他操作。 这些接口需要配合 `PhysicsRaycaster` 或 `GraphicRaycaster` 来检测用户的输入位置[^1]。 ```csharp using UnityEngine; using UnityEngine.EventSystems; public class DragObject : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler { private Vector3 offset; private Camera mainCamera; void Start() { mainCamera = Camera.main; } public void OnBeginDrag(PointerEventData eventData) { // 计算物体与屏幕坐标之间的偏移量 Vector3 screenPosition = new Vector3(eventData.position.x, eventData.position.y, transform.position.z); offset = transform.position - mainCamera.ScreenToWorldPoint(screenPosition)[^3]; } public void OnDrag(PointerEventData eventData) { // 更新物体位置 Vector3 newPosition = mainCamera.ScreenToWorldPoint(new Vector3(eventData.position.x, eventData.position.y, transform.position.z)); transform.position = newPosition + offset; } public void OnEndDrag(PointerEventData eventData) { // 结束拖拽后的处理逻辑 } } ``` --- #### 2. 添加必要的组件 为了让上述代码正常工作,需确保以下条件满足: - 物体必须附加一个碰撞器(Collider),以便能够被射线检测到。 - 场景中的摄像机需要添加 `Physics Raycaster` 组件[^2]。 - 如果希望支持 UI 元素的交互,则还需要为 Canvas 添加 `Graphic Raycaster`。 --- #### 3. 基于刚体的物理拖拽 如果物体附带了 `Rigidbody` 并且需要考虑物理特性(如碰撞、重力等),则推荐使用物理方法来更新物体位置。这种方法更加稳定,尤其适用于复杂场景下的动态模拟。 ```csharp private Rigidbody rb; private Vector3 offset; void Start() { rb = GetComponent<Rigidbody>(); } public void OnBeginDrag(PointerEventData eventData) { Vector3 screenPosition = new Vector3(eventData.position.x, eventData.position.y, transform.position.z); offset = transform.position - Camera.main.ScreenToWorldPoint(screenPosition)[^4]; } public void OnDrag(PointerEventData eventData) { Vector3 targetPosition = Camera.main.ScreenToWorldPoint(new Vector3(eventData.position.x, eventData.position.y, transform.position.z)) + offset; rb.MovePosition(targetPosition); // 利用 MovePosition 方法平滑更新位置 } ``` 此方法适合用于具有物理特性的对象,例如游戏中的角色或道具。 --- #### 4. 支持多平台触控 对于移动端设备,可以直接监听触摸事件而不依赖 Event System。这种方式无需额外配置组件即可运行,但灵活性较低。 ```csharp private bool isDragging = false; private Vector3 offset; void Update() { if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began) { Ray ray = Camera.main.ScreenPointToRay(Input.GetTouch(0).position); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { if (hit.collider.gameObject == this.gameObject) { isDragging = true; offset = transform.position - Camera.main.ScreenToWorldPoint(new Vector3(hit.point.x, hit.point.y, transform.position.z))[^5]; } } } if (isDragging && Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Moved) { Vector3 touchPos = Camera.main.ScreenToWorldPoint(new Vector3(Input.GetTouch(0).position.x, Input.GetTouch(0).position.y, transform.position.z)); transform.position = touchPos + offset; } if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Ended || Input.touchCount == 0) { isDragging = false; } } ``` --- ### 总结 以上提供了三种主要的方式实现 Unity3D 中的物体拖拽功能,分别是基于事件系统的接口调用、基于刚体的物理拖拽以及针对移动端的直接触控方案。开发者可以根据实际需求选择合适的实现路径。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值