Making edge colliders and polygon colliders snap to grid.

I think the title says it all. During development of a platformer type of game I have used the edge collider and polygon collider quite extensively and trough its use I have programmed a little script that automatically snaps all the points on both of the colliders to the nearest grid point. It would be great if the normal functionality of holding “control to snap to whole units” would also work for these components while editing the points on the collider. Maybe a smart check that sees if it should snap to the grid instead if applicable?

The script I use right now is posted below for everyone else to use:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(PolygonCollider2D))]
public class PolygonColliderSnapper : MonoBehaviour {

    public PolygonCollider2D polygon;

    // Use this for initialization
    void Start () {
        if (polygon == null)
            polygon = GetComponent<PolygonCollider2D>();
    }
   
    // Update is called once per frame
    void Update () {
   
    }

    public void Snap()
    {
        int counter = 0;
        for (int pathIndex = 0; pathIndex < polygon.pathCount; pathIndex++)
        {
            Vector2[] path = polygon.GetPath(pathIndex);
            for (long pointIndex = 0; pointIndex < path.Length; pointIndex++)
            {
                counter++;
                Vector2 point = path[pointIndex];
                path[pointIndex] = new Vector2(Mathf.RoundToInt(point.x), Mathf.RoundToInt(point.y));
            }
            polygon.points = path;
            polygon.SetPath(pathIndex, path);
        }
        print("Snapped " + counter + " points.");
    }
}

// Editor button script
using UnityEngine;
using System.Collections;
using UnityEditor;

[CustomEditor(typeof(PolygonColliderSnapper))]
public class ObjectBuilderEditor : Editor
{
    public override void OnInspectorGUI()
    {
        DrawDefaultInspector();

        PolygonColliderSnapper snapper = (PolygonColliderSnapper)target;
        if (GUILayout.Button("Snap Points"))
        {
            snapper.Snap();
        }
    }
}
1 Like

Yes, something to look into.

noob question: how do i use the script?

Thanks for the script, Tony-Tonijn. legoblaster1234, I got it to work like this:

  • Right click in Project tab to create 2 scripts, “PolygonColliderSnapper” and “ObjectBuilderEditor”.

  • Copy the respective code to their own script files (Lines 1-39 to PolygonColliderSnapper, and Lines 40-60 to ObjectBuilderEditor).

  • Add the PolygonColliderSnapper script to whatever GameObject you have your collider on. Drag the GameObject from the Hierarchy onto the Polygon slot of the Polygon Collider Snapper (Script) component in the Inspector.

  • Click on Snap Points and the collider points should update.

For those that don’t want to add a bunch of components in the scene to achieve this:

[MenuItem("Tools/Snap Poly Paths")]
static void SnapPolyPaths()
{
    var gos = EditorSceneManager.GetActiveScene().GetRootGameObjects();
    var count = 0;

    for (var i = 0; i < gos.Length; i++)
    {
        var go = gos[i];
        var polys = go.GetComponentsInChildren<PolygonCollider2D>(true);

        foreach (var poly in polys)
        {
            for (var n = 0; n < poly.pathCount; n++)
            {
                var path = poly.GetPath(n);

                for (var p = 0; p < path.Length; p++)
                {
                    var v2 = path[p];
                    var x = Mathf.Round(v2.x * 2f) / 2f; // Snaps every 0.5f
                    var y = Mathf.Round(v2.y * 2f) / 2f; // Snaps every 0.5f

                    path[p] = new Vector2(x, y);
                }

                poly.SetPath(n, path);
            }

            count++;
        }
    }

    EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());

    Debug.LogWarningFormat("Snapped {0} poly colliders.", count);
}
1 Like

A little changes for peoples in 2020 :smile: Make a c# script file with name: “MenuTest”. Create empty gameobject in Hierarchy and attach file script to gameobject. A new menu with name “MyMenu” will appear in Unity.

using UnityEditor;

using UnityEngine;
using UnityEngine.SceneManagement;
public class MenuTest : MonoBehaviour
{
    // Add a menu item named "Do Something" to MyMenu in the menu bar.
    [MenuItem("MyMenu/Collider")]
    static void SnapPolyPaths()
    {
        var gos = UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene().GetRootGameObjects();
        var count = 0;

        for (var i = 0; i < gos.Length; i++)
        {
            var go = gos[i];
            var polys = go.GetComponentsInChildren<PolygonCollider2D>(true);

            foreach (var poly in polys)
            {
                for (var n = 0; n < poly.pathCount; n++)
                {
                    var path = poly.GetPath(n);

                    for (var p = 0; p < path.Length; p++)
                    {
                        var v2 = path[p];
                        var x = Mathf.Round(v2.x * 2f) / 2f; // Snaps every 0.5f
                        var y = Mathf.Round(v2.y * 2f) / 2f; // Snaps every 0.5f

                        path[p] = new Vector2(x, y);
                    }

                    poly.SetPath(n, path);
                }

                count++;
            }
        }

        UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene());

        Debug.LogWarningFormat("Snapped {0} poly colliders.", count);
    }

}
1 Like

Hey what is up in 2020!
Here is a modified version of this script that I use for myself. It is based on the top post but it adds menu items for both PolygonCollider2d as well as EdgeCollider2d! I keep it in my editor folder as ColliderSnapper.cs

using UnityEditor;
using UnityEngine;
using UnityEditor.SceneManagement;
public class MenuTest : MonoBehaviour
{
    [MenuItem("Assets/Simiancraft/PolygonCollider2D Snapper")]
    private static void SnapPolyPaths()
    {
        var gos = EditorSceneManager.GetActiveScene().GetRootGameObjects();
        var polygonCount = 0;
        var pathCount = 0;
        for (var i = 0; i < gos.Length; i++)
        {
            var go = gos[i];
            var polys = go.GetComponentsInChildren<PolygonCollider2D>(true);
            foreach (var poly in polys)
            {
                for (var n = 0; n < poly.pathCount; n++)
                {
                    var path = poly.GetPath(n);
                    for (var p = 0; p < path.Length; p++)
                    {
                        var v2 = path[p];
                        var x = Mathf.Round(v2.x * 2f) / 2f;
                        var y = Mathf.Round(v2.y * 2f) / 2f;
                        path[p] = new Vector2(x, y);
                        pathCount++;
                    }
                    poly.SetPath(n, path);
                }
                polygonCount++;
            }
        }
        EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
        Debug.LogFormat("Snapped {0} paths across {1} poly colliders.", pathCount, polygonCount);
    }

    [MenuItem("Assets/Simiancraft/EdgeCollider2D Snapper")]
    private static void SnapEdgePoints()
    {
        var gos = EditorSceneManager.GetActiveScene().GetRootGameObjects();
        var edgeColliderCount = 0;
        var pointCount = 0;
        for (var i = 0; i < gos.Length; i++)
        {
            var go = gos[i];
            var edges = go.GetComponentsInChildren<EdgeCollider2D>(true);
            foreach (var edge in edges)
            {
                var points = edge.points;
                Vector2[] newPoints = new Vector2[points.Length];
                for (int pi = 0; pi < points.Length; pi++)
                {
                    var pt = points[pi];
                    var x = Mathf.Round(pt.x * 2f) / 2f;
                    var y = Mathf.Round(pt.y * 2f) / 2f;
                    newPoints[pi] = new Vector2(x, y);
                    pointCount++;
                }
                edge.points = newPoints;
                edgeColliderCount++;
            }
        }
        EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
        Debug.LogFormat("Snapped {0} points across {1} edge colliders.", pointCount, edgeColliderCount);
    }
}
1 Like