You need to enable JavaScript to run this app. Unity3D 实现地面物体Drag and Drop(仅修改X、Z轴位置)的方法求助
优惠活动
大模型
产品
解决方案
定价
更多

Unity3D 实现地面物体Drag and Drop(仅修改X、Z轴位置)的方法求助

Unity 地面物体固定Y轴拖拽功能实现方案

前期准备配置

  • 层级设置:在场景中创建空对象挂载核心拖拽脚本,将所有可交互地面设置为独立Ground层,为地面添加非Trigger类型的Collider组件
  • 可交互物体配置:给所有需要支持拖拽的物体添加非Trigger类型的Collider组件,确保物体初始位置的Y轴符合场景地面高度要求

核心实现代码

将以下脚本挂载到刚才创建的空对象上:

using UnityEngine;

public class GroundDragDrop : MonoBehaviour
{
    // 可拖拽物体对应的交互层,在Inspector面板配置
    [SerializeField] private LayerMask draggableLayer;
    [SerializeField] private Camera mainCam;

    private GameObject _selectedObject;
    private Vector3 _dragOffset;
    private float _fixedYPos;
    // 用于计算交点的水平平面:法向量朝上,固定匹配被拖拽物体的Y高度
    private Plane _dragPlane;

    private void Awake()
    {
        // 自动获取场景主摄像机
        if (mainCam == null)
            mainCam = Camera.main;
    }

    private void Update()
    {
        HandleDragStart();
        HandleDragging();
        HandleDragEnd();
    }

    private void HandleDragStart()
    {
        // 鼠标左键按下时检测是否点中可拖拽物体
        if (Input.GetMouseButtonDown(0))
        {
            Ray clickRay = mainCam.ScreenPointToRay(Input.mousePosition);
            if (Physics.Raycast(clickRay, out RaycastHit hit, Mathf.Infinity, draggableLayer))
            {
                _selectedObject = hit.collider.gameObject;
                // 记录选中物体的初始Y轴坐标,全程锁死不做修改
                _fixedYPos = _selectedObject.transform.position.y;
                // 初始化和物体同高度的水平拖拽平面
                _dragPlane = new Plane(Vector3.up, new Vector3(0, _fixedYPos, 0));
                // 计算点击时鼠标点与物体的位置偏移,避免拖拽时物体瞬间跳转到鼠标位置
                if (_dragPlane.Raycast(clickRay, out float enter))
                {
                    Vector3 clickWorldPos = clickRay.GetPoint(enter);
                    _dragOffset = _selectedObject.transform.position - clickWorldPos;
                }
            }
        }
    }

    private void HandleDragging()
    {
        // 按住鼠标时持续更新物体位置
        if (Input.GetMouseButton(0) && _selectedObject != null)
        {
            Ray dragRay = mainCam.ScreenPointToRay(Input.mousePosition);
            if (_dragPlane.Raycast(dragRay, out float enter))
            {
                Vector3 dragWorldPos = dragRay.GetPoint(enter);
                // 计算新位置:鼠标点位置 + 初始点击偏移,强制锁定Y轴坐标
                Vector3 newPos = dragWorldPos + _dragOffset;
                newPos.y = _fixedYPos;
                // 将计算好的位置赋值给物体
                _selectedObject.transform.position = newPos;
            }
        }
    }

    private void HandleDragEnd()
    {
        // 鼠标抬起时清空选中状态,结束拖拽
        if (Input.GetMouseButtonUp(0))
        {
            _selectedObject = null;
        }
    }
}

代码逻辑原理解释

针对性解决之前实现的常见问题

  • 拖拽手感异常(物体跳转到鼠标位置)问题:通过在点击选中瞬间记录鼠标世界坐标与物体位置的差值作为偏移量,后续位置计算始终叠加该偏移,保证物体被抓取时的相对位置不变,无突兀跳变
  • 位置计算不准问题:没有依赖地面碰撞体的射线返回结果计算位置,也没有直接调用容易出错的ScreenToWorldPoint方法(多数人实现时传入的Z值为鼠标的屏幕Z坐标,恒为0,导致计算点始终贴在摄像机近平面),而是通过数学定义的同高度水平无限平面计算鼠标射线交点,彻底避免拖拽过程中射线被选中物体自身、其他场景物体遮挡,或是坐标参数传错导致的位置跳变问题
  • Y轴不受控问题:在选中物体的第一时间记录物体的初始Y坐标,每次更新物体位置时强制将Y值设置为该固定值,完全不涉及Y轴的位置修改,符合仅调整X、Z轴的需求

关键实现细节

  • 拖拽平面的法向量为Vector3.up,平面经过的Y坐标和被选中物体的初始Y值完全一致,所有和平面求交得到的点都和物体保持同一水平高度,从根源上避免Y轴偏移
  • 层过滤配置:通过LayerMask指定可交互的物体范围,避免射线误选UI、静态场景装饰物等不需要交互的对象

使用说明

  • 脚本挂载后,在Inspector面板将Draggable Layer选项选为对应可拖拽物体所在的层,将主摄像机拖入Main Cam槽位即可生效
  • 如果需要实现拖拽范围限制(比如不能把物体拖出地图边界),只需要在HandleDragging方法中对计算得到的newPos.x和newPos.z做数值范围钳制即可

内容的提问来源于stack exchange,提问作者GGAndersson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.28 04:15:04