Search code examples
c#unity-game-enginepolygonbounds

How do I obtain the world-space position of PolygonCollider2D points?


I'm trying to write an editor tool involving PolygonCollider2D, but the problem is that the polygon points defined by the user in the editor do not correspond to PolygonCollider2D.bounds.center + PolygonCollider2D.points. According to the documentation, Bounds is in world-space, and the points are in local space, so to get the world-space location of the points, you need to add each point to the bounds.center, like this:

Vector2 p1 = (Vector2)polygon.bounds.center + polygon.points[i];

However, upon doing this and debug drawing the points, there is a difference between the UI-defined points and the mathematically derived points. The error gets bigger the larger you make the collider. Here is the script I was using to debug it.

List<Vector2> debugPositions = new();

public void OnSceneGUI()
    {
        for (int i = 0; i < debugPositions.Count; i++)
            Handles.DrawSolidRectangleWithOutline(new Rect(debugPositions[i].x, debugPositions[i].y, 0.2f, 0.2f), Color.cyan, Color.black);
    }

void GetPolygonPointsWorldPos(PolygonCollider2D polygon)
    {
        debugPositions.Clear();
        // Loop through the points of the polygon
        for (int i = 0; i < polygon.points.Length; i++)
        {
            //Get the world space pos of point and next point (wrap around if needed)
            Vector2 p1 = (Vector2)polygon.bounds.center + polygon.points[i];
            Vector2 p2 = (Vector2)polygon.bounds.center + polygon.points[(i + 1) % polygon.points.Length];
            debugPositions.Add(p1);
            debugPositions.Add(p2);
        }
    }

The only thing I can think of is that maybe the bounds.center is not actually the center, but I have tried using the transform.position as the center, and that didn't work either.


Solution

  • According to the documentation bounds.center is the center of the bounding box, meaning it's not the world position the points are relative to.

    I'm pretty sure the world position would simply be polygon.transform.position

    You should be able to get the world position of a point by saying

    polygon.transform.position + polygon.points[i]
    

    Editted for clarification:

    The image below shows a scene with only a PolygonCollider2D on a GameObject, and a script I used for drawing the points.

    • Cyan: The points in local space. The same as polygon.points[i]

    • Blue: The points in world space. The same as polygon.transform.position + polygon.points[i]

    • Green: The center of the points. This is the same as the position of the GameObject, i.e. polygon.transform.position

    • Red: The points on the bounding box. As you said, these are indeed in world space.

    • Yellow: The center of the bounding box. Note that this center is not the same as the center of the points.

    So as you can see, the position of the points in world space is relative to the GameObject's position, and not the bounding box's position.

    The code:

    public class PolygonColliderTest : MonoBehaviour
    {
        [SerializeField] private PolygonCollider2D polygon;
    
        private void OnDrawGizmos()
        {
            // Local position of the points
            Gizmos.color = Color.cyan;
            foreach (var point in polygon.points)
            {
                Gizmos.DrawSphere(point, 0.25f);
            }
    
            // World position of the points
            Gizmos.color = Color.blue;
            var ppos = polygon.transform.position;
            var center = new Vector2(ppos.x, ppos.y);
            foreach (var point in polygon.points)
            {
                Gizmos.DrawSphere(center + point, 0.25f);
            }
    
            // Center of the points in world space
            Gizmos.color = Color.green;
            Gizmos.DrawSphere(center, 0.25f);
    
            // Bounding box
            Gizmos.color = Color.red;
            var bcenter = polygon.bounds.center;
            var bmin = polygon.bounds.min;
            var bmax = polygon.bounds.max;
            var bp1 = bmin;
            var bp2 = bmax;
            var bp3 = new Vector3(bmin.x, bmax.y);
            var bp4 = new Vector3(bmax.x, bmin.y);
            var bps = new Vector3[4] { bp1, bp2, bp3, bp4 };
            foreach (var point in bps)
            {
                Gizmos.DrawSphere(point, 0.25f);
            }
    
            // Center of the bounding box in world space
            Gizmos.color = Color.yellow;
            Gizmos.DrawSphere(bcenter, 0.25f);
        }
    }