2
\$\begingroup\$

Hey guys I've been seriously stressed out by this, so please go easy on me.

My goal is to reflect a bullet (polygon) off of another polygon, and find the edge the another polygon, A (the bullet), hit. The collision code below partially works, thanks to this. I modified it a little to embed it into my code. The problem I'm facing is that the code below does not give me the edge of the polygon that the bullet hit. Through many resources like this, I was able to figure out the math behind what is presented in the picture below. I've done a lots of research, and am aware of the SAT, but am unable to extract the correct edge the projectile hit based off of the code.

Note that the picture presented has only rectangles, but this is aimed to work on all non-concave polygons.

enter image description here

I feel there's a fundamental understanding I'm missing, and not spotting it in the code, more specifically in the loops. To be clear, how am I able to modify the code below to return results where the bullet may act appropriately?

public class MainForm {

    // Structure that stores the results of the PolygonCollision function
    public struct PolygonCollisionResult {
        public bool Intersect; // Are the polygons currently intersecting
        public Vector NormalForceVector;
    }

    // Check if polygon A is going to collide with polygon B for the given velocity
    public static PolygonCollisionResult PolygonCollision(Polygon polygonA, Polygon polygonB, Vector velocity) {
        var result = new PolygonCollisionResult
        {
            Intersect = true
        };

        int edgeCountA = polygonA.Edges.Count;
        int edgeCountB = polygonB.Edges.Count;

        Vector[] edgesVectors = new Vector[edgeCountA + edgeCountB];

        //adds all edges to an array
        for (int edgeIndex = 0; edgeIndex < edgeCountA + edgeCountB; edgeIndex++)
        {
            if (edgeIndex < edgeCountA)
                edgesVectors[edgeIndex] = polygonA.Edges[edgeIndex];
            else
                edgesVectors[edgeIndex] = polygonB.Edges[edgeIndex - edgeCountA];

        }

        // Loop through all the edges of both polygons
        foreach(var edge in edgesVectors)
        {

            // Find the axis perpendicular to the current edge
            Vector axis = new Vector(-edge.Y, edge.X);
            axis.Normalize();

            // Find the projection of the polygon on the current axis
            float minA = 0; float minB = 0; float maxA = 0; float maxB = 0;
            ProjectPolygon(axis, polygonA, ref minA, ref maxA);
            ProjectPolygon(axis, polygonB, ref minB, ref maxB);

            // Check if the polygon projections are currentlty intersecting
            if (IntervalDistance(minA, maxA, minB, maxB) > 0)
                result.Intersect = false;

            // If the polygons are not intersecting, exit the loop
            if (!result.Intersect)
                break;



            //I need to return the edge hit and assign it to NormalForceVector
            result.NormalForceVector.X = ?????????;
            result.NormalForceVector.Y = ?????????;



        }

        return result;
    }


    //Two helper methods below that work perfectly fine:

    // Calculate the distance between [minA, maxA] and [minB, maxB]
    // The distance will be negative if the intervals overlap
    public static float IntervalDistance(float minA, float maxA, float minB, float maxB)
    {
        if (minA < minB)
            return minB - maxA;

        return minA - maxB;
    }

    // Calculate the projection of a polygon on an axis and returns it as a [min, max] interval
    public static void ProjectPolygon(Vector axis, Polygon polygon, ref float min, ref float max)
    {
        // To project a point on an axis use the dot product
        float d = axis.DotProduct(polygon.Points[0]);
        min = d;
        max = d;
        for (int i = 0; i < polygon.Points.Count; i++)
        {
            d = polygon.Points[i].DotProduct(axis);
            if (d < min)
            {
                min = d;
            }
            else
            {
                if (d > max)
                {
                    max = d;
                }
            }
        }
    }
}

This loop here is where I'm cycling through all of the polygons, checking for collision.

foreach (var polyEntity in RunGame.Level.ObjListPolygon)
        {

            var resultStruct = MainForm.PolygonCollision(_bulletPolygon, polyEntity, new Vector(_velocity.X, _velocity.Y));

            if (resultStruct.Intersect)
            {

                //here is where i'm using that normal force
                var normalForce = new Vector2(resultStruct.NormalForceVector.X, resultStruct.NormalForceVector.Y);
                normalForce.Normalize();

                _velocity = Vector2.Reflect(_velocity, normalForce);


                _angle = (float)Math.Atan2(_velocity.Y, _velocity.X);

                break;
            }
        }

From debugging, I know that the foreach loop where it iterates through each polygon edge returns the last edge in the loop. Because this code returns that edge (which it should not), I'm only gaining sufficient results for that edge. In the future, my goal is to add not only polygons, but circles/ellipses as well (in the far future). Does anyone know what's going wrong? Anything helps.

\$\endgroup\$
  • \$\begingroup\$ Please comment if anything is unclear, or if more information is needed. Thanks! \$\endgroup\$ – Eric Jul 14 '16 at 4:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.