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

Unity中如何获取不平坦地形上方固定高度的鼠标世界位置?

解决Unity俯视角游戏中鼠标世界位置距地面1.5f的问题

原代码的问题

你当前的代码逻辑是获取射线与地面的交点,然后在射线上找到Y坐标为交点Y+1.5的点。但在斜坡等不平坦地形上,这个点的XZ坐标和交点不同,其正下方的地面高度会比交点Y值高,导致实际距离地面的高度小于1.5f,无法满足需求。

解决方案思路

我们需要找到射线上的某一点,使得该点的Y坐标减去其正下方地面的Y坐标恰好等于1.5f。可以通过迭代调整射线上的位置,逐步逼近符合条件的点。

针对Terrain地形的实现

如果你的地面用Unity原生Terrain组件,直接用SampleHeight获取地面高度,性能更高效:

private Vector3 GetMousePosition()
{
    Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition);
    
    if (Physics.Raycast(ray, out RaycastHit hitInfo, Mathf.Infinity))
    {
        // 以初始交点的t值为起点
        float t = (hitInfo.point - ray.origin).magnitude / ray.direction.magnitude;
        
        // 迭代5次,确保结果足够准确
        for (int i = 0; i < 5; i++)
        {
            Vector3 currentPoint = ray.origin + t * ray.direction;
            // 获取当前点XZ位置的地面高度(包含Terrain的世界位置偏移)
            float groundHeight = Terrain.activeTerrain.SampleHeight(new Vector3(currentPoint.x, 0, currentPoint.z)) + Terrain.activeTerrain.transform.position.y;
            float heightDiff = currentPoint.y - groundHeight;
            
            // 高度差误差小于0.01f时停止迭代
            if (Mathf.Abs(heightDiff - 1.5f) < 0.01f)
                break;
            
            // 调整t值,让当前点的Y向目标高度差靠近
            t += (1.5f - heightDiff) / ray.direction.y;
        }
        
        return ray.origin + t * ray.direction;
    }
    else
    {
        // 未击中地面时的 fallback 逻辑,确保点距地面1.5f
        Vector3 fallbackPoint = transform.position + ray.direction * 150f;
        float groundHeight = Terrain.activeTerrain.SampleHeight(new Vector3(fallbackPoint.x, 0, fallbackPoint.z)) + Terrain.activeTerrain.transform.position.y;
        fallbackPoint.y = groundHeight + 1.5f;
        return fallbackPoint;
    }
}

针对Mesh/Collider地面的通用实现

如果地面是自定义Mesh加Collider,通过向下发射射线检测地面高度:

private Vector3 GetMousePosition()
{
    Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition);
    
    if (Physics.Raycast(ray, out RaycastHit hitInfo, Mathf.Infinity))
    {
        float t = (hitInfo.point - ray.origin).magnitude / ray.direction.magnitude;
        
        for (int i = 0; i < 5; i++)
        {
            Vector3 currentPoint = ray.origin + t * ray.direction;
            // 从当前点向下发射射线,检测地面
            Ray downRay = new Ray(currentPoint, Vector3.down);
            
            if (Physics.Raycast(downRay, out RaycastHit downHit, Mathf.Infinity))
            {
                float heightDiff = currentPoint.y - downHit.point.y;
                if (Mathf.Abs(heightDiff - 1.5f) < 0.01f)
                    break;
                
                t += (1.5f - heightDiff) / ray.direction.y;
            }
            else
            {
                // 向下未击中地面,说明当前点在地面下方,减小t值
                t -= 1f;
            }
        }
        
        return ray.origin + t * ray.direction;
    }
    else
    {
        Vector3 fallbackPoint = transform.position + ray.direction * 150f;
        Ray downRay = new Ray(fallbackPoint, Vector3.down);
        
        if (Physics.Raycast(downRay, out RaycastHit downHit))
        {
            fallbackPoint.y = downHit.point.y + 1.5f;
        }
        else
        {
            // 仍未击中地面,直接添加高度偏移
            fallbackPoint.y += 1.5f;
        }
        
        return fallbackPoint;
    }
}

代码说明

  • 迭代次数设为5次足够平衡精度和性能,你可以根据需求调整。
  • 初始t值用射线与地面的交点计算,减少迭代次数。
  • 每次迭代根据当前高度差调整t值,逐步让点的Y坐标满足地面高度+1.5f的要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 00:50:05