通过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;
}
}