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
相关产品推荐
相关产品推荐

