Automatically Create Polygon Collider 2D From 2D Mesh in Unity

Sep 6, 2016ProgrammingComments (14)

I was working on a 2D game in Unity and needed some way to automatically create a collider for a complex 2D level mesh. Editing a

To use it, create a ColliderCreator C# script, copy the code in, and add the Script component to a GameObject that has a 2D mesh. When you hit Play, the script will generate a Polygon Collider 2D for that GameObject based on the mesh. While the game is running, it's best to save the generated PolygonCollider2D using

The old version of the code was much slower. You can still see it below, but I highly recommend using the above one.

*Polygon Collider 2D*in the editor was out of the question, since there were thousands of vertices. I ended up writing a script that analyzes the MeshFilter of the mesh, and programmatically creates a Polygon Collider 2D for it. It also supports meshes that have mutliple non-connected components.**How to Use It**To use it, create a ColliderCreator C# script, copy the code in, and add the Script component to a GameObject that has a 2D mesh. When you hit Play, the script will generate a Polygon Collider 2D for that GameObject based on the mesh. While the game is running, it's best to save the generated PolygonCollider2D using

*Copy Component*, and paste it into the prefab. Then the script won't have to run every time.**ColliderCreator C# Code**`using System.Collections.Generic;`

using UnityEngine;

public class ColliderCreator : MonoBehaviour

{

void Start()

{

__// Stop if no mesh filter exists or there's already a collider__

if (GetComponent<PolygonCollider2D>() || GetComponent<MeshFilter>() == null) {

return;

}

__// Get triangles and vertices from mesh__

int[] triangles = GetComponent<MeshFilter>().mesh.triangles;

Vector3[] vertices = GetComponent<MeshFilter>().mesh.vertices;

__// Get just the outer edges from the mesh's triangles (ignore or remove any shared edges)__

Dictionary<string, KeyValuePair<int, int>> edges = new Dictionary<string, KeyValuePair<int, int>>();

for (int i = 0; i < triangles.Length; i += 3) {

for (int e = 0; e < 3; e++) {

int vert1 = triangles[i + e];

int vert2 = triangles[i + e + 1 > i + 2 ? i : i + e + 1];

string edge = Mathf.Min(vert1, vert2) + ":" + Mathf.Max(vert1, vert2);

if (edges.ContainsKey(edge)) {

edges.Remove(edge);

} else {

edges.Add(edge, new KeyValuePair<int, int>(vert1, vert2));

}

}

}

__// Create edge lookup (Key is first vertex, Value is second vertex, of each edge)__

Dictionary<int, int> lookup = new Dictionary<int, int>();

foreach (KeyValuePair<int, int> edge in edges.Values) {

if (lookup.ContainsKey(edge.Key) == false) {

lookup.Add(edge.Key, edge.Value);

}

}

__// Create empty polygon collider__

PolygonCollider2D polygonCollider = gameObject.AddComponent<PolygonCollider2D>();

polygonCollider.pathCount = 0;

__// Loop through edge vertices in order__

int startVert = 0;

int nextVert = startVert;

int highestVert = startVert;

List<Vector2> colliderPath = new List<Vector2>();

while (true) {

__// Add vertex to collider path__

colliderPath.Add(vertices[nextVert]);

__// Get next vertex__

nextVert = lookup[nextVert];

__// Store highest vertex (to know what shape to move to next)__

if (nextVert > highestVert) {

highestVert = nextVert;

}

__// Shape complete__

if (nextVert == startVert) {

__// Add path to polygon collider__

polygonCollider.pathCount++;

polygonCollider.SetPath(polygonCollider.pathCount - 1, colliderPath.ToArray());

colliderPath.Clear();

__// Go to next shape if one exists__

if (lookup.ContainsKey(highestVert + 1)) {

__// Set starting and next vertices__

startVert = highestVert + 1;

nextVert = startVert;

__// Continue to next loop__

continue;

}

__// No more verts__

break;

}

}

}

}

## Old Version

The old version of the code was much slower. You can still see it below, but I highly recommend using the above one.

`using UnityEngine;`

using System.Collections.Generic;

public class ColliderCreator : MonoBehaviour

{

private int currentPathIndex = 0;

private PolygonCollider2D polygonCollider;

private List<Edge> edges = new List<Edge>();

private List<Vector2> points = new List<Vector2>();

private Vector3[] vertices;

void Start()

{

__// Get the polygon collider (create one if necessary)__

polygonCollider = GetComponent<PolygonCollider2D>();

if (polygonCollider == null) {

polygonCollider = gameObject.AddComponent<PolygonCollider2D>();

}

__// Get the mesh's vertices for use later__

vertices = GetComponent<MeshFilter>().mesh.vertices;

__// Get all edges from triangles__

int[] triangles = GetComponent<MeshFilter>().mesh.triangles;

for (int i = 0; i < triangles.Length; i += 3) {

edges.Add(new Edge(triangles[i], triangles[i + 1]));

edges.Add(new Edge(triangles[i + 1], triangles[i + 2]));

edges.Add(new Edge(triangles[i + 2], triangles[i]));

}

__// Find duplicate edges__

List<Edge> edgesToRemove = new List<Edge>();

foreach (Edge edge1 in edges) {

foreach (Edge edge2 in edges) {

if (edge1 != edge2) {

if (edge1.vert1 == edge2.vert1 && edge1.vert2 == edge2.vert2 || edge1.vert1 == edge2.vert2 && edge1.vert2 == edge2.vert1) {

edgesToRemove.Add(edge1);

}

}

}

}

__// Remove duplicate edges (leaving only perimeter edges)__

foreach (Edge edge in edgesToRemove) {

edges.Remove(edge);

}

__// Start edge trace__

edgeTrace(edges[0]);

}

void edgeTrace(Edge edge)

{

__// Add this edge's vert1 coords to the point list__

points.Add(vertices[edge.vert1]);

__// Store this edge's vert2__

int vert2 = edge.vert2;

__// Remove this edge__

edges.Remove(edge);

__// Find next edge that contains vert2__

foreach (Edge nextEdge in edges) {

if (nextEdge.vert1 == vert2) {

edgeTrace(nextEdge);

return;

}

}

__// No next edge found, create a path based on these points__

polygonCollider.pathCount = currentPathIndex + 1;

polygonCollider.SetPath(currentPathIndex, points.ToArray());

__// Empty path__

points.Clear();

__// Increment path index__

currentPathIndex ++;

__// Start next edge trace if there are edges left__

if (edges.Count > 0) {

edgeTrace(edges[0]);

}

}

}

class Edge

{

public int vert1;

public int vert2;

public Edge(int Vert1, int Vert2)

{

vert1 = Vert1;

vert2 = Vert2;

}

}