Hi everyone,
I’m working on generating an outline mesh in Unity based on a PolygonCollider2D
, but I’m stepping into a strange issue. I have a script that takes a polygon collider and generates a mesh outline with a specified thickness. The issue I’m seeing is that some of the edges (marked with arrows in the attached image) look thinner than expected, even though I’m applying the same thickness throughout. Most of the edges have the correct thickness, but a few don’t match up.
I suspect that the issue may have something to do with how the normals are being calculated or the way triangles are generated, but I’m not sure what’s causing this inconsistency.
Has anyone encountered something similar or have any suggestions on what might be going wrong?
Here’s the portion of the script that handles the mesh generation:
private Mesh GenerateOutlineMeshFromCollider(PolygonCollider2D polygonCollider,
float thickness) {
if (polygonCollider == null) {
Debug.LogError("Selected GameObject does not have a PolygonCollider2D component!");
return null;
}
Mesh mesh = new Mesh();
Polygon2D poly = new Polygon2D(polygonCollider.points);
Vector2[] points = poly.GetTranslatedToOrigin();
int pointCount = points.Length;
Vector2[] normals = poly.CalculateOutwardNormals();
Vector3[] vertices = new Vector3[pointCount * 2];
int[] triangles = new int[(pointCount * 2) * 3];
// Calculate vertices
for (int i = 0; i < pointCount; i++) {
Vector2 p = points[i];
Vector2 outer = p;
Vector2 inner = p + normals[i] * thickness;
vertices[i * 2] = new Vector3(inner.x, inner.y, 0);
vertices[i * 2 + 1] = new Vector3(outer.x, outer.y, 0);
// Debug.Log(
// $"{i}: P{prevPoint} {currentPoint} {nextPoint} N{inwardNormal} Vi{vertices[i *
// 2 + 1]} Vo{vertices[i * 2]}");
}
// Calculate triangles for each edge
for (int i = 0; i < pointCount - 1; i++) {
int index = i * 6; // 6 indices (2 triangles) per edge
// Inner triangle
triangles[index] = i * 2;
triangles[index + 1] = i * 2 + 1;
triangles[index + 2] = i * 2 + 2;
// Outer triangle
triangles[index + 3] = i * 2 + 1;
triangles[index + 4] = i * 2 + 3;
triangles[index + 5] = i * 2 + 2;
}
// Close the loop for the last edge
int lastIndex = (pointCount - 1) * 6;
triangles[lastIndex] = (pointCount - 1) * 2;
triangles[lastIndex + 1] = (pointCount - 1) * 2 + 1;
triangles[lastIndex + 2] = 0;
triangles[lastIndex + 3] = (pointCount - 1) * 2 + 1;
triangles[lastIndex + 4] = 1;
triangles[lastIndex + 5] = 0;
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.RecalculateNormals();
mesh.RecalculateBounds();
return mesh;
}
(please ignore the fact that the meaning of outer
and inner
above is reversed as I had been messing with the normals)
And this is how I calculate outward normals and vertices:
public Vector2[] CalculateOutwardNormals() {
Vector2[] normals = new Vector2[length];
for (int i = 0; i < length; i++) {
Vector2 currentPoint = points[i];
Vector2 prevPoint = points[(i - 1 + length) % length];
Vector2 nextPoint = points[(i + 1) % length];
Vector2 edgeToPrev = prevPoint - currentPoint;
Vector2 edgeToNext = nextPoint - currentPoint;
// Check for zero-length or very small edges
if (edgeToPrev.magnitude < Mathf.Epsilon || edgeToNext.magnitude < Mathf.Epsilon) {
normals[i] = Vector2.zero;
continue;
}
edgeToPrev.Normalize();
edgeToNext.Normalize();
Vector2 normalToPrev = new Vector2(-edgeToPrev.y, edgeToPrev.x);
Vector2 normalToNext = new Vector2(-edgeToNext.y, edgeToNext.x);
Vector2 normal = normalToPrev - normalToNext;
normals[i] = normal.magnitude < Mathf.Epsilon ? normal : normal.normalized;
}
return normals;
}
Screenshot below illustrates the issue. Lines in red facing out are a representation of the normals. Shapes in white are the generated outlines; the shapes above the generated outlines are the polygon colliders. Thickness should be the exact same for all edges in both generated outlines.