0
\$\begingroup\$

I have a ball, which should bounce when it collides with a surface. I know the formula for reflections;

(v1 - 2(v1 • n)n)


However, I cannot obtain the required information. I know:

  • the ball position,
  • the ball velocity,
  • the ball radius,
  • the polygon center
  • the polgygon width and height
  • the polgyon corner coordinates

Diagram


How does one calculate the surface normal, in 2D collisions?

\$\endgroup\$
  • 1
    \$\begingroup\$ Does the accepted answer here help? stackoverflow.com/questions/1966587/… \$\endgroup\$ – Maximus Minimus Jan 19 '17 at 22:27
  • \$\begingroup\$ Thanks buddy but I think the example posted is for 3D space which ironically is a lot easier I think. OR maybe I just dont understand that example :). See marked answer, and my answer below for a working solution. \$\endgroup\$ – あらまあ Jan 20 '17 at 18:49
  • \$\begingroup\$ 2D space is the exact same except operating on 2D vectors (alternatively replace Z with 0). \$\endgroup\$ – Maximus Minimus Jan 21 '17 at 9:25
3
\$\begingroup\$

I think the following may help. You have the sphere center, box center and hopefully the details of the rectangle. Since the rectangle may be rotated we need the rectangle extents, and the orthogonal unit vectors, e.g.

enter image description here

Now we need the closest point on the rectangle to the point c.

vector d = c - r; 

// project d onto ux to get distance along ux from c
float dx = dot(d,ux)
if(dx > ex)    dx = ex;
if(dx < -ex)   dx = -ex;

// project d onto uy to get distance along uy from c
float dy = dot(d,uy)
if(dy > ey)    dy = ey;
if(dy < -ey)   dy = -ey;

// calculate closest point p on box to c
vector p = r + dx*ux + dy*uy;

vector collision_norm = norm(c - p);

Now this should hopefully give you the required collision normal.

\$\endgroup\$
  • 1
    \$\begingroup\$ This drawing is gorgeous. \$\endgroup\$ – Quentin Jan 20 '17 at 12:43
  • \$\begingroup\$ Hi there, thanks so much for your answer. Unfortunately, I have not come across the term 'extent' and I'm not sure how I would go about obtaining this value. Is it a vector from center out towards the edge? How do I know from which edge to project? Thanks. \$\endgroup\$ – あらまあ Jan 20 '17 at 16:16
  • \$\begingroup\$ You have the polygon (rectangle) width and height, so the extents are ex = width/2 and ey = height/2. The terms come from a definition of a Oriented Bounding Box (OBB) where you can define a rotated box by its center (r), orthonormal unit vectors (ux,uy) and the distance along those vectors to the sides of the rectangles (ex,ey). \$\endgroup\$ – Biggy Smith Jan 20 '17 at 16:32
  • \$\begingroup\$ Hi Biggy, Thanks. Im trying to implement this but it only appears to be working when the ball hits the center of the rectangle, so I think I have done it wrong. Edit to come... \$\endgroup\$ – あらまあ Jan 20 '17 at 17:21
  • \$\begingroup\$ Ok, So I managed to get it working - with some modifications to ensure it works within the engine I'm using. See my posted answer for the source. Thanks very much for your help! :) \$\endgroup\$ – あらまあ Jan 20 '17 at 18:25
0
\$\begingroup\$

Based on the answer posted by Biggy Smith - See engine specific CPP code below.

CVector Helper::GetNormal(CSprite* collisionSource, CSprite* collisiontarget)
{
    // Work out the displacement vector
    auto d = collisionSource->GetPos() - collisiontarget->GetPos();

    // Find the extants of the rectangle
    auto ex = collisiontarget->GetWidth() / 2.0f;
    auto ey = collisiontarget->GetHeight() / 2.0f;

    // Find the bottom right point of the rectangle
    CVector bottomRight = CVector(collisiontarget->GetRight(), collisiontarget->GetBottom());

    // Find the bottom left point of the rectangle
    CVector bottomLeft = collisiontarget->GetBottomLeft();

    // Find the top left point of the rectangle
    CVector topLeft = CVector(collisiontarget->GetLeft(), collisiontarget->GetTop());

    // Find orthonomal unit vectors (I think? These are normalised anyway...)
    // In this instance I just took the displacement of the two appropriate corner points.
    CVector ux = Normalise(bottomRight - bottomLeft);
    CVector uy = Normalise(topLeft - bottomLeft);

    // Project d onto ux to get distance along ux from c
    float dx = Dot(d, ux);

    if (dx > ex) 
    { 
        dx = ex;
    }
    else if (dx < -ex)
    { 
        dx = -ex;
    }

    // Project d onto uy to get distance along uy from c
    float dy = Dot(d, uy);

    if (dy > ey)
    {
        dy = ey;
    }
    else if (dy < -ey)
    {
        dy = -ey;
    }

    // calculate closest point p on box to c
    CVector contactPoint = collisiontarget->GetPosition() + dx*ux + dy*uy;

    // Calculate the normal at this point
    CVector surfaceNormal = Normalise(collisionSource->GetPosition() - contactPoint);

    return CVector(round(surfaceNormal.X()), round(surfaceNormal.Y()));
}
\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.