? ? ? ? 要實現人物腳下圓形?完美貼合復雜地形(如彈坑)?的效果,核心思路是?「動態生成貼合地面的 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 或代碼實現邊緣閃爍、顏色漸變(如選中時高亮)。