要实现人物脚下圆形 完美贴合复杂地形(如弹坑) 的效果,核心思路是 「动态生成贴合地面的 Mesh」 —— 即根据地面的高度场实时计算环形顶点的 Y 坐标,让每个顶点都 “贴” 在地面上。
核心逻辑:
- 确定环形范围:以人物脚部为中心,设定半径(如 1 米)和分段数(如 36 段,决定平滑度)。
- 生成环形顶点:在 XY 平面生成环形的顶点坐标(极坐标转笛卡尔坐标),然后 对每个顶点,实时获取其在地面的 Y 高度。
- 构建 / 更新 Mesh:将顶点组成环形 Mesh(三角面或线框),每帧或人物移动时,重新计算顶点的 Y 坐标,确保贴合地面。
关键步骤实现:
1. 地面高度采样(核心!)
- 对 Unity Terrain:用
Terrain.SampleHeight(xzPosition)
直接获取地形高度。 - 对 Mesh 地面:发射射线检测地面,获取
RaycastHit.point.y
(更通用,支持任意模型地面)。 float GetGroundY(float x, float z) {Vector3 origin = new Vector3(x, characterFoot.position.y + 10f, z); // 从高处向下射Ray ray = new Ray(origin, Vector3.down);if (Physics.Raycast(ray, out RaycastHit hit, 20f, groundLayers)) {return hit.point.y; // 地面高度}return characterFoot.position.y; // fallback }
2. 动态生成环形 Mesh
- 顶点生成:以中心为原点,用极坐标生成环形顶点的 X、Z 坐标,再通过上述方法获取 Y 坐标。
- 三角面构建:将顶点组成三角面(填充环形)或线框(仅边缘)。
- 实时更新:人物移动或地面变化时,重新计算顶点的 Y 坐标,更新 Mesh。
- 示例代码(简化版,生成填充环形):
using System.Collections; using System.Collections.Generic; using UnityEngine;using UnityEngine;public class tmp : MonoBehaviour {public Transform characterFoot;public float radius = 1f;public int segments = 36;public LayerMask groundLayers;private Mesh mesh;private Vector3[] vertices;private int[] triangles;private MeshFilter meshFilter;private MeshRenderer meshRenderer;void Start(){// 获取或添加MeshFilter组件meshFilter = GetComponent<MeshFilter>();if (meshFilter == null)meshFilter = gameObject.AddComponent<MeshFilter>();// 获取或添加MeshRenderer组件(用于显示网格)meshRenderer = GetComponent<MeshRenderer>();if (meshRenderer == null)meshRenderer = gameObject.AddComponent<MeshRenderer>();mesh = new Mesh();meshFilter.mesh = mesh;meshRenderer.sharedMaterial = new Material(Shader.Find("Universal Render Pipeline/Unlit"));UpdateMesh();}void UpdateMesh(){if (meshFilter == null || characterFoot == null) return;vertices = new Vector3[segments + 1];vertices[0] =new Vector3(0, GetGroundY(0f, 0f) - characterFoot.position.y + 0.03f, 0);//vertices[0] = characterFoot.position;for (int i = 1; i <= segments; i++){float angle = Mathf.Deg2Rad * (i * 360f / segments);//float x = characterFoot.position.x + Mathf.Cos(angle) * radius;//float z = characterFoot.position.z + Mathf.Sin(angle) * radius;float x = Mathf.Cos(angle) * radius;float z = Mathf.Sin(angle) * radius;float y = GetGroundY(x, z) - characterFoot.position.y+0.03f;vertices[i] = new Vector3(x, y, z);}triangles = new int[segments * 3];for (int i = 0; i < segments; i++){triangles[i * 3] = 0; // 中心点triangles[i * 3 + 1] = i + 2; // 下一个顶点triangles[i * 3 + 2] = i + 1; // 当前顶点}triangles[segments * 3 - 2] = 1; // 最后补上循环mesh.vertices = vertices;mesh.triangles = triangles;mesh.RecalculateNormals();Vector3[] normals = mesh.normals;mesh.normals = normals;}void Update(){if (meshFilter == null || vertices == null) return;//vertices[0] = new Vector3(0, GetGroundY(0f, 0f) - characterFoot.position.y + 0.03f, 0);for (int i = 0; i <= segments; i++){Vector3 pos = vertices[i];pos.y = GetGroundY(pos.x, pos.z)- characterFoot.position.y + 0.03f;Debug.Log(pos.y);vertices[i] = pos;}mesh.vertices = vertices;mesh.RecalculateNormals();}float GetGroundY(float x, float z){if (characterFoot == null) return 0;// 3D射线起点:在角色脚下位置上方10单位处Vector3 origin = new Vector3(characterFoot.position.x+x, characterFoot.position.y + 10f, characterFoot.position.z+z);// 射线方向和长度Vector3 direction = Vector3.down;float maxDistance = 20f;// 绘制射线(仅在编辑模式和Play模式的Scene视图可见)// 颜色设为红色,持续0.1秒(足够看清且不会残留太久)Debug.DrawRay(origin, direction * maxDistance, Color.red, 0.1f);// 3D射线检测RaycastHit hit;if (Physics.Raycast(origin, direction, out hit, maxDistance, groundLayers)){// 绘制碰撞点标记(绿色小球)Debug.DrawRay(hit.point, Vector3.up * 0.5f, Color.green, 0.1f);return hit.point.y;}// 未检测到地面时,绘制射线终点标记(黄色小球)Debug.DrawRay(origin + direction * maxDistance, Vector3.up * 0.5f, Color.yellow, 0.1f);return characterFoot.position.y;}}
3. 视觉优化(Shader + 纹理)
- 纹理:使用带 Alpha 通道的环形纹理(中间透明,边缘黄色渐变)。
- Shader:采用
Transparent
shader,支持 Alpha 混合,可添加 自发光(Emission) 效果让环形更醒目。 - 动画:通过 Shader 或代码实现边缘闪烁、颜色渐变(如选中时高亮)。