Unity中如何修改选中三角形的相邻三角形颜色?
实现选中三角形的相邻三角形临时高亮方案

你已经实现了选中三角形的着色功能,要添加相邻三角形临时高亮的功能,可以按以下步骤修改代码:
核心思路
- 提前构建三角形邻接表:在Awake时预处理,记录每个三角形的相邻三角形索引,避免每次点击都遍历整个三角形数组,提升性能
- 临时修改颜色+协程恢复:找到相邻三角形后,保存它们的原始颜色,修改为目标颜色,再通过协程在指定时间后恢复
- 优化顶点分离逻辑:现有代码中顶点复制的判断条件错误(
verticesOccur >=1),应该改为verticesOccur >=2,因为每个顶点至少属于当前选中的三角形,只有被多个三角形共享时才需要分离
修改后的完整代码
using System.Collections.Generic; using System.Linq; using UnityEngine; public class MyRayDraw : MonoBehaviour { public GameObject spherePrefab; [SerializeField] private MeshFilter meshFilter; [SerializeField] private MeshRenderer meshRenderer; [SerializeField] private MeshCollider meshCollider; [SerializeField] private float highlightDuration = 3f; // 高亮持续时间 [SerializeField] private Color adjacentTriangleColor = Color.white; // 相邻三角形高亮颜色 [SerializeField] private Color selectedTriangleColor = Color.red; // 选中三角形颜色 private Mesh mesh; private List<int>[] triangleAdjacency; // 三角形邻接表,每个元素存储对应三角形的相邻三角形索引 private Color[] originalColors; // 保存原始颜色,用于恢复 private void Awake() { if (!meshFilter) meshFilter = GetComponent<MeshFilter>(); if (!meshRenderer) meshRenderer = GetComponent<MeshRenderer>(); if (!meshCollider) meshCollider = GetComponent<MeshCollider>(); mesh = meshFilter.mesh; originalColors = new Color[mesh.vertices.Length]; // 初始化所有顶点颜色为初始色 for (var i = 0; i < originalColors.Length; i++) { originalColors[i] = new Color(1f, 0f, 0.07f); } mesh.colors = originalColors; // 构建三角形邻接表 BuildTriangleAdjacency(); } /// <summary> /// 构建三角形邻接关系:每个三角形对应的相邻三角形列表 /// </summary> private void BuildTriangleAdjacency() { var triangles = mesh.triangles; int triangleCount = triangles.Length / 3; triangleAdjacency = new List<int>[triangleCount]; // 先初始化每个三角形的邻接列表 for (int i = 0; i < triangleCount; i++) { triangleAdjacency[i] = new List<int>(); } // 创建边到三角形的映射:键是边的两个顶点索引(按从小到大排序避免重复),值是使用该边的三角形索引 Dictionary<(int, int), List<int>> edgeToTriangles = new Dictionary<(int, int), List<int>>(); for (int triIndex = 0; triIndex < triangleCount; triIndex++) { // 获取当前三角形的三个顶点索引 int v0 = triangles[triIndex * 3]; int v1 = triangles[triIndex * 3 + 1]; int v2 = triangles[triIndex * 3 + 2]; // 处理三条边 AddEdgeToDict(edgeToTriangles, v0, v1, triIndex); AddEdgeToDict(edgeToTriangles, v1, v2, triIndex); AddEdgeToDict(edgeToTriangles, v2, v0, triIndex); } // 遍历边映射,每个被两个三角形共享的边,对应的两个三角形互为相邻 foreach (var kvp in edgeToTriangles) { if (kvp.Value.Count == 2) { int triA = kvp.Value[0]; int triB = kvp.Value[1]; if (!triangleAdjacency[triA].Contains(triB)) { triangleAdjacency[triA].Add(triB); } if (!triangleAdjacency[triB].Contains(triA)) { triangleAdjacency[triB].Add(triA); } } } } /// <summary> /// 将边添加到字典中,确保边的顶点索引按升序存储,避免重复 /// </summary> private void AddEdgeToDict(Dictionary<(int, int), List<int>> dict, int vA, int vB, int triIndex) { var edgeKey = vA < vB ? (vA, vB) : (vB, vA); if (!dict.ContainsKey(edgeKey)) { dict[edgeKey] = new List<int>(); } dict[edgeKey].Add(triIndex); } private void Update() { if (!Input.GetMouseButtonDown(0)) return; var ray = Camera.main.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out RaycastHit hit)) { int selectedTriIndex = hit.triangleIndex; Debug.Log($"选中三角形索引:{selectedTriIndex}"); // 1. 处理选中三角形的着色(分离共享顶点,避免影响其他三角形) SeparateAndColorSelectedTriangle(selectedTriIndex); // 2. 获取相邻三角形并临时高亮 StartCoroutine(HighlightAdjacentTriangles(selectedTriIndex)); } else { Debug.Log("未命中任何物体"); } } /// <summary> /// 分离选中三角形的共享顶点,并着色 /// </summary> private void SeparateAndColorSelectedTriangle(int triIndex) { var vertices = mesh.vertices.ToList(); var triangles = mesh.triangles.ToList(); var colors = mesh.colors.ToList(); // 获取选中三角形的三个顶点索引 int v0 = triangles[triIndex * 3]; int v1 = triangles[triIndex * 3 + 1]; int v2 = triangles[triIndex * 3 + 2]; // 计算每个顶点被多少个三角形共享 int countV0 = triangles.Count(x => x == v0); int countV1 = triangles.Count(x => x == v1); int countV2 = triangles.Count(x => x == v2); // 分离共享顶点:只有被多个三角形使用时才需要复制 if (countV0 >= 2) { vertices.Add(mesh.vertices[v0]); colors.Add(selectedTriangleColor); triangles[triIndex * 3] = vertices.Count - 1; } else { colors[v0] = selectedTriangleColor; } if (countV1 >= 2) { vertices.Add(mesh.vertices[v1]); colors.Add(selectedTriangleColor); triangles[triIndex * 3 + 1] = vertices.Count - 1; } else { colors[v1] = selectedTriangleColor; } if (countV2 >= 2) { vertices.Add(mesh.vertices[v2]); colors.Add(selectedTriangleColor); triangles[triIndex * 3 + 2] = vertices.Count - 1; } else { colors[v2] = selectedTriangleColor; } // 更新网格数据 mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.colors = colors.ToArray(); mesh.RecalculateNormals(); // 更新碰撞体(如果需要射线检测仍然有效) meshCollider.sharedMesh = mesh; } /// <summary> /// 高亮相邻三角形,并在指定时间后恢复 /// </summary> private IEnumerator HighlightAdjacentTriangles(int selectedTriIndex) { var colors = mesh.colors.ToList(); Dictionary<int, Color> originalTriangleColors = new Dictionary<int, Color>(); // 获取所有相邻三角形 List<int> adjacentTriangles = triangleAdjacency[selectedTriIndex]; // 保存相邻三角形的原始颜色,并修改为高亮颜色 foreach (int triIndex in adjacentTriangles) { int v0 = mesh.triangles[triIndex * 3]; int v1 = mesh.triangles[triIndex * 3 + 1]; int v2 = mesh.triangles[triIndex * 3 + 2]; // 保存每个顶点的原始颜色 originalTriangleColors[v0] = colors[v0]; originalTriangleColors[v1] = colors[v1]; originalTriangleColors[v2] = colors[v2]; // 设置高亮颜色 colors[v0] = adjacentTriangleColor; colors[v1] = adjacentTriangleColor; colors[v2] = adjacentTriangleColor; } mesh.colors = colors.ToArray(); // 等待指定时长 yield return new WaitForSeconds(highlightDuration); // 恢复原始颜色 foreach (var kvp in originalTriangleColors) { colors[kvp.Key] = kvp.Value; } mesh.colors = colors.ToArray(); } }
关键修改说明
- 邻接表构建:通过边映射的方式,高效找出每个三角形的相邻三角形,避免每次点击都遍历整个三角形数组
- 顶点分离优化:修正了原代码中顶点复制的判断条件,只有当顶点被多个三角形共享时才复制,减少不必要的顶点数量
- 临时高亮与恢复:使用协程实现定时恢复,保存相邻三角形顶点的原始颜色,确保高亮结束后能准确恢复
- 可配置参数:将高亮时长、高亮颜色等参数暴露在Inspector面板,方便调试
内容的提问来源于stack exchange,提问作者newCoderMe
相关产品推荐
相关产品推荐

