I have taken the task of building a basic impulse physics engine. Circle vs Circle collision resolution has been done and I moved onto Line vs Circle. I can detect a collision and know that the normal should be perpendicular to the line which can be calculated easily (2d physics engine). However it's the direction of the normal that I cannot work out. enter image description here

From the image you can see there are 2 possible normals, one upwards (the correct one) and one downwards. How do I choose the correct normal?


Generally you'd check which side of the line your circle is on, and flip the normal if it's pointing away from the circle:

Vector2 outward = circle.center - line.startPoint;

if(Vector2.Dot(outward, normal) < 0f)
   return -1f * normal;

return normal;

This is susceptible to an error called "tunneling," where a small, fast-moving circle might move into and juuuuust past the line in a single update. Then, even if it came from the top side, our check above will see that the circle is currently on the bottom side and produce a downward-pointing normal instead.

For extreme cases of tunneling (where the circle hops all the way over the line and we never detect the collision at all), there's not much we can do except reduce the timestep, give the colliders some "thickness," or switch to more expensive continuous collision detection.

But for slight tunneling we can sometimes do better by checking against the incoming velocity instead of position - assuming that if we've encountered a collision, then whichever way the circle was moving should be opposite the surface normal. That would look like this:

// Compute relative velocity of the circle from the perspective of the line.
Vector2 inward = circle.velocity - line.velocity;

if(Vector2.Dot(inward, normal) > 0f)
   return -1f * normal;

return normal;

This can run into errors if you have multiple collisions being resolved in sequence, if an earlier collision resolution modified the velocity so it's not pointing in the direction that originally caused the collision. So watch out for that, and make sure you're looking at the snapshot of the velocity from the impact, not from a subsequent rebound.

  • \$\begingroup\$ Damn, you were faster than me 😂 That's a perfect answer. \$\endgroup\$ Aug 19 '18 at 20:37
  • \$\begingroup\$ Haha, sorry @FerreiradaSelva. Feel free to post your answer too - the more the merrier. And there's certainly more than one way to skin this cat. \$\endgroup\$
    – DMGregory
    Aug 19 '18 at 20:38
  • \$\begingroup\$ @DMGregory I had already tried the relative velocity along the normal but came to some issues. When the circle began to move away from the line it would think it is on the other side and pull it back into the line. So the ball would be stuck in place sort of inside the line. The first solution seems to be working well though :) \$\endgroup\$
    – cheiffeast
    Aug 19 '18 at 20:48
  • \$\begingroup\$ Yep, that's the case I mentioned above with "make sure you're looking at the snapshot of the velocity from the impact, not from a subsequent rebound." Some engines do this by applying an immediate position correction to move the objects out of collision in one frame. Others distinguish between when frames a collision starts vs when a collision is still in-progress. \$\endgroup\$
    – DMGregory
    Aug 19 '18 at 20:59

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.