You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 19:30:08