6
\$\begingroup\$

I have a circle, that bounces of walls. The circle is traveling at the speed defined by px/s for x and y. I have collision detection implemented and I use vectors to calculate the new x/y speed (and direction) after the collision. My problem is, that is some cases the circle gets stuck in or is consumed by the wall.

The reason for this is, that when the collision occurs, the circle is actually x pixels inside the wall (as illustrated by the image below) and not just at the "border" of the wall. This occurs because the circle might move multiple pixels on each redraw (tick). The circle doesn't move enough pixels during the next redraw in order to get loose from the wall, thus triggering a new collision. Direction changes again towards the wall leading to a shaking motion of the circle, either finally getting loose of the wall or being consumed by the wall.

Circle has collided with the wall and is partially inside the wall

The solution for this is to reposition the circle so that it is loose from the wall after the collision has occurred. My problem is, that with my rusty mathematics, I can't figure out how to calculate the correct position of the circle and that's where I need your help.

I also wonder, how exact I need to be with the position, I mean, I know the minimum distance of the middle of the circle to the wall (and thus also know how much the circle has penetrated the wall), let's call this value d. I could of course move the circle d pixels perpendicular to the wall, this might be a close enough approximation of the position, although not 100% accurate. If you guys think this is a good enough solution, is there a way to achieve this with vectors or do I need to calculate the angles and new position "manually" using cos and sin?

Side note: I'm not using SAT for collision detection because I have concave polygons.

\$\endgroup\$
6
\$\begingroup\$

You are right in your assumptions of what needs to be done! In physics engines, after a collision was detected but before the collision is resolved (The changing of the objects velocity) there are a few steps which need to be done.

One of these steps is what I call decoupling: The process of separating two intersecting objects. This is the stage you are at. You have seen the results of neglecting to decouple intersecting objects when a collision is detected, and you have a good gage on what must be done to perform this decoupling.

You mention a value d, the penetration depth of the circle into the wall. You can actually calculate this depth pretty simply.

  1. Take two points on your wall as vectors, A, and B and the center of your circle to be C.

  2. Get the direction vector of the wall dir = normalize(B - A);. Then, get the vector from a point on your wall to the center of the circle toCenter = C - A;.

  3. Now, you can use the projection to figure out the penetration depth! The projection of toCenter onto dir will give you the component vector of toCenter parallel to the wall. If you take this parallel component, and subtract it from the total, you are left with the vector which is perpendicular to the wall.

perpVec = toCenter - projection(toCenter, dir);

  1. At this point, your penetration depth will be the radius of the circle minus the magnitude of the perpVec!

d = radius - mag(perpVec)

  1. And if you move the circle d units in the direction of perpVec you can be left with your decoupled objects!

If you have trouble with the mathematics behind these operations, or their meaning and why exactly this gives you the proper solution, please let me know in the comments I would be happy to help.

To wrap it up and put a bow on it for you:

Vector dir = normalize(B - A);
Vector toCenter = (C - A);

Vector perpComponent = toCenter - dot(toCenter, dir) * dir; //toCenter - Projection

float penetrationDepth = radius - mag(perpComponent);

Vector mvmtToCorrectPosition = penetrationDepth * normalize(perpComponent);

Edit

A co-worker of mine, and myself have been continuing work on a slew of game programming examples. In the repository we actually have an explicit example of this but with two circles instead of a circle and a line:

https://github.com/MrNex/Game-Programming-Examples/tree/master/GLFW/Collision%20Resolution/Decoupling%20Intersecting%20Objects%20%28Circle%20-%202D%29/Decoupling%20Intersecting%20Objects%20%28Circle%20-%202D%29

Check out the main.cpp file. And if you are interested in the same example but with more complex convex polygons as colliding objects:

https://github.com/MrNex/Game-Programming-Examples/tree/master/GLFW/Collision%20Resolution/Decoupling%20Intersecting%20Objects%20%28Convex%20Hull%20-%202D%29/Decoupling%20Intersecting%20Objects%20%28Convex%20Hull%20-%202D%29

If you find these examples helpful please check the rest of the repository for a slew of stand alone game/graphics/physics programming examples in both 2D and 3D.

\$\endgroup\$
  • \$\begingroup\$ I'm not sure I understand. The movement of the ball to get it out of the wall will never be seen by the user. It happens after a collision is detected but before the next draw call. All this is meant to do is not allow the ball to enter the wall. The velocities should not have anything to do with how far two objects are intersecting. There is a flaw, however-- known as tunnelling. IF the ball is moving very very fast it can travel so far into the wall that the perpendicular component will push it further into the wall (and potentially out of the other side). \$\endgroup\$ – Mr. Nex Aug 7 '15 at 19:50
  • \$\begingroup\$ Nevermind my comment. I thought this would make the ball move faster in high angled walls, but I was wrong. \$\endgroup\$ – dimitris93 Aug 7 '15 at 20:02
  • \$\begingroup\$ Thanks for the reply, @Mr.Nex, however, I seem to have some problems with the math. I keep getting a penetration depth of negative values whose absolute values are larger than the radius - seems odd to me. I guess negative value just indicates the direction of the movement? Also, just to make sure, should I add the movement vector (mvmtToCorrectPosition) to the current position in order to get the corrected circle position? \$\endgroup\$ – Kim L Aug 9 '15 at 5:48
  • \$\begingroup\$ The penetration depth should not be larger than the radius (or negative for that matter). perpComponent, geometrically, is the vector from the closest point on the line making up the wall to the center of the sphere. For penetrationDepth to be negative, I believe it would signify that the distance from the wall to the sphere's center is larger than radius which would imply no collision. Can you update your answer with the new code and let me take a look? You may also want to post the collision detection code itself. \$\endgroup\$ – Mr. Nex Aug 10 '15 at 9:10
  • \$\begingroup\$ On an unrelated note, yes the final step would be adding the movement vector to the current position of the sphere to get the final corrected position. \$\endgroup\$ – Mr. Nex Aug 10 '15 at 9:21

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.