Unity中实现点击拖拽并保持初始偏移量的技术问题
解决拖拽偏移问题及代码改进
核心问题解决:保持光标与物体的初始偏移
当前代码直接将物体移动到光标对应的世界位置,导致物体瞬间跳转到光标中心。要解决这个问题,需要在拖拽开始时记录光标点击位置相对于物体锚点的偏移量,后续拖拽时用光标位置减去这个偏移量作为物体的目标位置。
修改后的完整代码
// 存储拖拽时光标与物体的初始偏移 private Vector3 _dragOffset; // 缓存WaitForFixedUpdate,避免重复创建对象 private WaitForFixedUpdate _waitForFixedUpdate; private void Awake() { _waitForFixedUpdate = new WaitForFixedUpdate(); // 提前缓存主相机,避免反复调用Camera.main的性能开销 mainCamera = Camera.main; } private void MousePressed(InputAction.CallbackContext context) { // 仅在鼠标按下的瞬间触发,避免重复启动协程 if (!context.performed) return; Vector2 mousePosition = Input.mousePosition; Vector3 worldPosition = mainCamera.ScreenToWorldPoint(new Vector3(mousePosition.x, mousePosition.y, -mainCamera.transform.position.z)); worldPosition.z = 0; Collider2D hitCollider = Physics2D.OverlapPoint(worldPosition); if (hitCollider != null) { GameObject clickedObject = hitCollider.gameObject; // 计算光标相对于物体的偏移:点击位置 - 物体当前位置 _dragOffset = worldPosition - clickedObject.transform.position; StartCoroutine(DragUpdate(clickedObject)); } } private IEnumerator DragUpdate(GameObject clickedObject) { // 物体被销毁时直接终止协程,避免空引用报错 if (clickedObject == null) yield break; float initialDistance = Vector3.Distance(clickedObject.transform.position, mainCamera.transform.position); bool hasRigidbody = clickedObject.TryGetComponent<Rigidbody2D>(out var rb); // 将velocity改为局部变量,避免全局变量被多协程污染 Vector2 velocity = Vector2.zero; while (mouseClick.ReadValue<float>() != 0) { Ray ray = mainCamera.ScreenPointToRay(Mouse.current.position.ReadValue()); // 计算目标位置:光标对应世界位置 - 初始偏移,保持相对位置不变 Vector3 targetPosition = ray.GetPoint(initialDistance) - _dragOffset; targetPosition.z = 0; // 锁定2D平面的Z轴 if (hasRigidbody) { Vector3 direction = targetPosition - clickedObject.transform.position; rb.velocity = direction * mouseDragPhysicsSpeed; yield return _waitForFixedUpdate; } else { clickedObject.transform.position = Vector2.SmoothDamp(clickedObject.transform.position, targetPosition, ref velocity, mouseDragSpeed); yield return null; } } }
关键修改点
- 在
MousePressed中计算并存储_dragOffset:记录光标点击位置与物体当前位置的差值,确保拖拽过程中物体始终保持这个相对位置。 - 在
DragUpdate中调整目标位置计算逻辑:用targetPosition = ray.GetPoint(initialDistance) - _dragOffset替代原直接使用光标位置,避免物体跳转到光标中心。 - 新增
Awake方法缓存主相机和WaitForFixedUpdate,减少重复操作的性能开销。
其他代码改进建议
- 输入触发控制:增加
context.performed判断,确保仅在鼠标按下瞬间启动一次拖拽协程,避免重复触发。 - 空值安全防护:在
DragUpdate开头增加物体空值检查,防止物体被销毁后协程报错。 - 变量作用域优化:将
velocity改为局部变量,避免全局变量被多次拖拽操作干扰。 - 命名规范统一:私有成员变量使用下划线开头的驼峰命名(如
_dragOffset),区分局部变量,提升代码可读性。 - 协程管理:如果存在快速连续点击场景,可以维护当前拖拽协程的引用,启动新协程前停止旧协程,避免多协程同时运行冲突。
内容的提问来源于stack exchange,提问作者soleil
相关产品推荐
相关产品推荐

