📌 前提条件
- 场景中必须有 EventSystem(除非使用
OnMouseDrag)。
- 拖拽目标必须能被事件系统或物理系统检测到:
- UI 元素:Canvas + GraphicRaycaster。
- 3D 物体:Collider + Camera 上的 PhysicsRaycaster。
核心代码
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。