通过PolygonCollider2D生成多边形网格面片

在编辑器状态下生成多边形网格面片

网格生成脚本

通过EidtorWindow创建预制体,预制体需要挂载MeshFilter和MeshRenderer

点击查看代码
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

[RequireComponent(typeof(PolygonCollider2D))]
public class CreatePolygonMesh : MonoBehaviour
{
    [SerializeField] private PolygonCollider2D polygonCollider2D;
    [SerializeField] private MeshFilter meshFilter;
    public int PointsCount => polygonCollider2D.points.Length;

    public void Create_Mesh_For_PolygonCollider2D()
    {
        //获取多边形碰撞体的顶点
        var vVertices = new Vector3[polygonCollider2D.points.Length];
        for (var i = 0; i < polygonCollider2D.points.Length; i++)
        {
            var points = polygonCollider2D.points;
            vVertices[i] = new Vector3(points[i].x, points[i].y, 0);
        }

        DoCreatPolygonMesh(vVertices);
    }

    private void DoCreatPolygonMesh(Vector3[] vertices)
    {
        //新建一个空物体进行进行绘制自定义多边形
        var tMesh = new Mesh();
        var tTriangles = Calculate_Triangles_CutEar_Method(vertices);
        tMesh.vertices = vertices;
        tMesh.triangles = tTriangles.ToArray();
        tMesh.RecalculateBounds();
        tMesh.RecalculateNormals();
        meshFilter.mesh = tMesh;
    }

    private List<int> Calculate_Triangles_CutEar_Method(Vector3[] vertices)
    {
        var vVertices = new List<Vector3>();
        for (var i = 0; i < vertices.Length; i++)
        {
            vVertices.Add(new Vector3(vertices[i].x, vertices[i].y, i));
        }

        var tTriangles = new List<int>();
        var sn = 0;
        var layer = 0;
        while (vVertices.Count > 3 && layer < 1000)
        {
            var vPoints = vVertices.Select(t => (Vector2) t).ToList();
            var sna = sn - 1;
            if (sna < 0)
                sna = vPoints.Count - 1;
            var snb = sn;
            var snc = sn + 1;
            if (snc >= vPoints.Count)
                snc = 0;
            var vTriangle = new List<Vector2>
            {
                vPoints[sna],
                vPoints[snb],
                vPoints[snc]
            };
            var blHaveOtherPointInTriangle = vPoints.Where((_, i) => i != sna && i != snb && i != snc)
                .Any(t => Check_Point_InPolygon(t, vTriangle));
            var blCutEar = false;
            if (blHaveOtherPointInTriangle == false)
            {
                var vCenter = (vTriangle[0] + vTriangle[1] + vTriangle[2]) / 3;
                if (Check_Point_InPolygon(vCenter, vPoints))
                {
                    var ta = (int) vVertices[sna].z;
                    var tb = (int) vVertices[snb].z;
                    var tc = (int) vVertices[snc].z;
                    tTriangles.Add(tc);
                    tTriangles.Add(tb);
                    tTriangles.Add(ta);
                    vVertices.RemoveAt(snb);
                    blCutEar = true;
                }
            }

            if (blCutEar == false)
            {
                sn++;
            }

            if (sn >= vVertices.Count)
                sn = 0;
            layer++;
        }

        if (vVertices.Count == 3)
        {
            var ta = (int) vVertices[0].z;
            var tb = (int) vVertices[1].z;
            var tc = (int) vVertices[2].z;
            tTriangles.Add(tc);
            tTriangles.Add(tb);
            tTriangles.Add(ta);
        }

        Debug.Log("tTriangles.Count : " + tTriangles.Count + "  layer : " + layer);
        return tTriangles;
    }

    private bool Check_Point_InPolygon(Vector2 vCheck, IReadOnlyList<Vector2> vPoints)
    {
        //从检查点向右做线,若碰到两点之间的线段,则num_across++
        var numAcross = 0;
        var count = 0;
        var i = 0;
        while (count < vPoints.Count)
        {
            var v1 = vPoints[i];
            if (i == vPoints.Count - 1)
                i = -1;
            var v2 = vPoints[i + 1];

            switch (Math.Abs(v1.y - v2.y) <= 0)
            {
                case false when Math.Abs(v1.y - vCheck.y) <= 0 && v1.x >= vCheck.x:
                case false when Math.Abs(v2.y - vCheck.y) <= 0 && v2.x >= vCheck.x:
                    numAcross++;
                    break;
                case false when Math.Abs(Mathf.Sign(v1.y - vCheck.y) - Mathf.Sign(v2.y - vCheck.y)) <= 0:
                    break;
                case false:
                {
                    var xLine = v1.x + (v2.x - v1.x) * (vCheck.y - v1.y) / (v2.y - v1.y);
                    if (xLine >= vCheck.x)
                    {
                        numAcross++;
                    }

                    break;
                }
            }

            i++;
            count++;
        }

        return (numAcross % 2) switch
        {
            1 => true,
            _ => false
        };
    }
}

编辑器下检测更改

通过EditorApplication.update每帧对比PolygonCollider2D顶点值的变化,实现在编辑器状态下动态生成多边形网格

点击查看代码
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;

[InitializeOnLoad]
public class MapEditorTool
{
    private static PolygonCollider2D _previousCollider;
    private static Vector2[] _previousPoints;

    static MapEditorTool()
    {
        EditorApplication.update += OnEditorUpdate;
    }

    private static void OnEditorUpdate()
    {
        var selectedObject = Selection.activeGameObject;
        if (selectedObject == null) return;
        var targetScript = selectedObject.GetComponent<CreatePolygonMesh>();
        if (targetScript == null) return;
        var currentCollider = selectedObject.GetComponent<PolygonCollider2D>();
        if (currentCollider == null) return;
        if (currentCollider != _previousCollider)
        {
            _previousCollider = currentCollider;
            _previousPoints = (Vector2[]) currentCollider.points.Clone();
        }
        else
        {
            if (ComparePoints(currentCollider.points, _previousPoints)) return;
            _previousPoints = (Vector2[]) currentCollider.points.Clone();
            targetScript.Create_Mesh_For_PolygonCollider2D();
        }
    }

    private static bool ComparePoints(IReadOnlyCollection<Vector2> pointsA, IReadOnlyList<Vector2> pointsB)
    {
        if (pointsA.Count != pointsB.Count) return false;
        return !pointsA.Where((t, i) => t != pointsB[i]).Any();
    }

    public static void ReSetSelectObj(GameObject select)
    {
        Selection.activeGameObject = select;
        SceneView.lastActiveSceneView.FrameSelected();
        SceneView.lastActiveSceneView.in2DMode = true;
    }
}

效果

image

posted @ 2024-04-01 15:11  瞌睡的小牛  阅读(12)  评论(0编辑  收藏  举报