You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中如何修改选中三角形的相邻三角形颜色?

实现选中三角形的相邻三角形临时高亮方案

Unity场景截图

你已经实现了选中三角形的着色功能,要添加相邻三角形临时高亮的功能,可以按以下步骤修改代码:

核心思路

  1. 提前构建三角形邻接表:在Awake时预处理,记录每个三角形的相邻三角形索引,避免每次点击都遍历整个三角形数组,提升性能
  2. 临时修改颜色+协程恢复:找到相邻三角形后,保存它们的原始颜色,修改为目标颜色,再通过协程在指定时间后恢复
  3. 优化顶点分离逻辑:现有代码中顶点复制的判断条件错误(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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 08:15:32