🎮 Unity 拖动物体技术文档

📌 前提条件

  • 场景中必须有 EventSystem(除非使用 OnMouseDrag)。
  • 拖拽目标必须能被事件系统或物理系统检测到:
    • UI 元素:Canvas + GraphicRaycaster。
    • 3D 物体:Collider + Camera 上的 PhysicsRaycaster。

🖼 拖拽 UI 元素(Canvas 内的 Image/Button)

核心代码

using UnityEngine;
using UnityEngine.EventSystems;

public class UIDrag : MonoBehaviour, IBeginDragHandler, IDragHandler
{
    private Vector2 offset;
    public void OnBeginDrag(PointerEventData eventData)
    {
        offset = new Vector2(transform.position.x, transform.position.y) - eventData.position;
    }
    public void OnDrag(PointerEventData eventData)
    {
        transform.position = eventData.position + offset;
    }
}

特点

  • 无需 Collider
  • 无需 PhysicsRaycaster,只要 Canvas 上有 GraphicRaycaster
  • 坐标直接使用 eventData.position,不需要考虑 z 深度。

🎲 拖拽 3D 世界物体(使用 IDragHandler)

核心代码

using UnityEngine;
using UnityEngine.EventSystems;

public class Drag3DObject : MonoBehaviour, IBeginDragHandler, IDragHandler
{
    private Vector3 offset;

    public void OnBeginDrag(PointerEventData eventData)
    {
        Vector3 worldPos = Camera.main.ScreenToWorldPoint(
            new Vector3(eventData.position.x, eventData.position.y,
                        Camera.main.WorldToScreenPoint(transform.position).z));
        offset = transform.position - worldPos;
    }

    public void OnDrag(PointerEventData eventData)
    {
        Vector3 pos = new Vector3(eventData.position.x, eventData.position.y,
                                  Camera.main.WorldToScreenPoint(transform.position).z);
        transform.position = Camera.main.ScreenToWorldPoint(pos) + offset;
    }
}

特点

  • 必须有 Collider
  • Camera 上需要挂 PhysicsRaycaster
  • 通过 ScreenToWorldPoint 将屏幕坐标转换为世界坐标。
  • 使用 offset 避免物体在拖拽时“跳到鼠标中心”。

🎯 射线检测拖拽 3D 物体

核心代码

using UnityEngine;

public class RaycastDrag : MonoBehaviour
{
    private Transform selectedObject;
    private Vector3 offset;

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;

            if (Physics.Raycast(ray, out hit, 1000, 1 << LayerMask.NameToLayer("Draggable")))
            {
                selectedObject = hit.transform;
                Vector3 worldPos = Camera.main.ScreenToWorldPoint(
                    new Vector3(Input.mousePosition.x, Input.mousePosition.y,
                                Camera.main.WorldToScreenPoint(selectedObject.position).z));
                offset = selectedObject.position - worldPos;
            }
        }

        if (Input.GetMouseButton(0) && selectedObject != null)
        {
            Vector3 pos = new Vector3(Input.mousePosition.x, Input.mousePosition.y,
                                      Camera.main.WorldToScreenPoint(selectedObject.position).z);
            selectedObject.position = Camera.main.ScreenToWorldPoint(pos) + offset;
        }

        if (Input.GetMouseButtonUp(0))
        {
            selectedObject = null;
        }
    }
}

特点

  • 灵活,可选择任意 3D 物体。
  • 不依赖 EventSystem,直接用物理系统。
  • 适合 FPS 射击、RTS 单位选择、场景交互。

🖱️ OnMouseDrag 拖拽 3D 物体

核心代码

using UnityEngine;

public class MouseDragExample : MonoBehaviour
{
    private Vector3 offset;

    void OnMouseDown()
    {
        Vector3 worldPos = Camera.main.ScreenToWorldPoint(
            new Vector3(Input.mousePosition.x, Input.mousePosition.y,
                        Camera.main.WorldToScreenPoint(transform.position).z));
        offset = transform.position - worldPos;
    }

    void OnMouseDrag()
    {
        Vector3 pos = new Vector3(Input.mousePosition.x, Input.mousePosition.y,
                                  Camera.main.WorldToScreenPoint(transform.position).z);
        transform.position = Camera.main.ScreenToWorldPoint(pos) + offset;
    }
}

特点

  • 物体必须有 Collider
  • 不需要 EventSystem。
  • 简单快速,但只能响应鼠标,无法处理触摸/多点交互。

⚖️ 四种方式对比

方法 优点 缺点 常见用途
UI 拖拽 (IDragHandler) 简单,直接用事件系统;无需考虑 z 仅限 UI 元素 拖拽 UI 面板、图片
3D 拖拽 (IDragHandler) 与 UI 拖拽统一;支持触摸 依赖 EventSystem + PhysicsRaycaster 少量 3D 拖拽场景
射线检测 灵活,可选择任意物体;适合复杂交互 逻辑稍复杂,需要写 Update FPS 射击、RTS 单位选择
OnMouseDrag 简单,不需要 EventSystem 只能响应鼠标,移动端不适用 快速实现 3D 拖拽

🎯 总结

  • UI 元素拖拽 → 用 IDragHandler
  • 简单 3D 拖拽 → 用 OnMouseDrag
  • 复杂 3D 场景交互 → 用 射线检测
  • 跨平台(鼠标+触摸)3D 拖拽 → 用 IDragHandler + PhysicsRaycaster。

posted @ 2025-12-19 20:32  高山仰止666  阅读(0)  评论(0)    收藏  举报