Unity中如何获取顺序正确的顶点世界坐标?

Unity中获取顺序正确的省份边界顶点世界坐标
我有一些代表省份边界的顶点,希望在Unity中获取它们顺序正确的世界坐标。目前用以下代码能获取顶点世界坐标,但顺序不正确,通过LineRenderer渲染时无法正确呈现边界。
[SerializeField] private MeshFilter _mesh; [SerializeField] private LineRenderer _lineRenderer; [Serializable] public class Border { public Mesh borderMesh; public BorderProvince[] sharedBetween = new BorderProvince[2]; public bool isACountryBorder; } [SerializeField] private List<Border> _borders; private List<Vector3> _drawnBorder = new List<Vector3>(); [ContextMenu("Update Border Drawer")] private void UpdateBorderDrawer() { if (_borders.Count == 0 || _lineRenderer == null) return; _drawnBorder = new List<Vector3>(); Matrix4x4 localToWorld = transform.localToWorldMatrix; foreach (Border border in _borders) { if (border.sharedBetween[0].ID != border.sharedBetween[1].ID) { border.isACountryBorder = true; for (int i = 0; i < border.borderMesh.vertices.Length; i++) { _drawnBorder.Add(localToWorld.MultiplyPoint3x4(border.borderMesh.vertices[i])); } } } _lineRenderer.positionCount = _drawnBorder.Count; _lineRenderer.SetPositions(_drawnBorder.ToArray()); }
解决思路及方案
Mesh的顶点数组本身是无序的,它的顺序基于三角面索引排列,而非边界的连续路径顺序。要得到正确的边界顶点顺序,需从Mesh的拓扑结构中重建连续路径:
提取边界边并构建连续路径
遍历Mesh三角面索引,收集仅出现一次的边界边(内部边会被两个三角面共享),再通过邻接关系拼接出连续的顶点路径:private List<Vector3> GetOrderedBorderVertices(Mesh mesh, Matrix4x4 localToWorld) { List<Vector3> orderedVertices = new List<Vector3>(); if (mesh == null) return orderedVertices; // 统计所有边的出现次数 Dictionary<(int, int), int> edgeCount = new Dictionary<(int, int), int>(); int[] triangles = mesh.triangles; for (int i = 0; i < triangles.Length; i += 3) { AddEdge(triangles[i], triangles[i+1], edgeCount); AddEdge(triangles[i+1], triangles[i+2], edgeCount); AddEdge(triangles[i+2], triangles[i], edgeCount); } // 筛选出仅出现一次的边界边 List<(int, int)> borderEdges = edgeCount.Where(kv => kv.Value == 1).Select(kv => kv.Key).ToList(); if (borderEdges.Count == 0) return orderedVertices; // 构建顶点邻接表 Dictionary<int, List<int>> adjacency = new Dictionary<int, List<int>>(); foreach (var edge in borderEdges) { if (!adjacency.ContainsKey(edge.Item1)) adjacency[edge.Item1] = new List<int>(); adjacency[edge.Item1].Add(edge.Item2); if (!adjacency.ContainsKey(edge.Item2)) adjacency[edge.Item2] = new List<int>(); adjacency[edge.Item2].Add(edge.Item1); } // 拼接连续路径 int currentVertex = borderEdges[0].Item1; int previousVertex = -1; orderedVertices.Add(localToWorld.MultiplyPoint3x4(mesh.vertices[currentVertex])); while (orderedVertices.Count < borderEdges.Count) { foreach (int nextVertex in adjacency[currentVertex]) { if (nextVertex != previousVertex) { orderedVertices.Add(localToWorld.MultiplyPoint3x4(mesh.vertices[nextVertex])); previousVertex = currentVertex; currentVertex = nextVertex; break; } } } return orderedVertices; } private void AddEdge(int a, int b, Dictionary<(int, int), int> edgeCount) { // 按索引大小排序,确保边的唯一性 var edge = a < b ? (a, b) : (b, a); edgeCount[edge] = edgeCount.ContainsKey(edge) ? edgeCount[edge] + 1 : 1; }修改原更新方法
将原代码中直接添加顶点的逻辑替换为上述有序顶点获取方法:[ContextMenu("Update Border Drawer")] private void UpdateBorderDrawer() { if (_borders.Count == 0 || _lineRenderer == null) return; _drawnBorder = new List<Vector3>(); Matrix4x4 localToWorld = transform.localToWorldMatrix; foreach (Border border in _borders) { if (border.sharedBetween[0].ID != border.sharedBetween[1].ID) { border.isACountryBorder = true; // 获取有序顶点并添加到列表 var orderedVertices = GetOrderedBorderVertices(border.borderMesh, localToWorld); _drawnBorder.AddRange(orderedVertices); } } _lineRenderer.positionCount = _drawnBorder.Count; _lineRenderer.SetPositions(_drawnBorder.ToArray()); }额外注意事项
- 若Border Mesh包含多个独立边界线段,需拆分处理每个路径
- 可在编辑器中提前预处理顶点顺序,减少运行时计算开销
内容的提问来源于stack exchange,提问作者Adam Brown
相关产品推荐
相关产品推荐

