Conceptually there are eight cases that can occur if you're bouncing a circular object off of a rectangular one. Four of those are 'object hits one of the flat walls', and the other four are 'object hits a corner'. Doing the collision with the corners is probably not necessary unless you're going for a game with precise physics that really matter to the game (like pool). However, if you want to do corner collisions, here's how:
- First, check if the bounding box of your object overlaps with the tile. If not, no collision.
- If the bounding box overlaps, check for each wall whether the center of your moving object is directly above the wall and whether that center is closer to the wall than the object's radius. For example, a tile whose extents are from (tx,ty) to (tx+w, ty+h) colliding with an object whose coordinates are (x,y) with radius r:
if ( (x>=tx)&&(x<=tx+w)&&(y+r<ty+h)&&(y+r>ty) )
// collision with top wall
...
- If you have not collided with any of the flat segments, check the distance between your object center and all of the vertices at the corners of the tiles. If this is <= r, you have a corner collision. For a corner collision, you have some vector N = (x-cx, y-cy) where cx and cy are the coordinates of the corner. You want to update your velocity like so:
v' = v-2*(N dot v)*N / |N|^2
If you want an inelastic collision, replace 2 with (1-E), where E=0 is totally inelastic and epsilon=1 is E.
There's a bit of a subtle point to the corner collisions - you want to exclude vertices where two solid tiles meet, or you can get weird bounces at the tile intersections depending on the order you evaluate collisions in.
All of this is complicated by the fact that for finite timestep size, your object will slightly overlap the tile when you're determining collision, which can cause things like 'teleporting through a corner' or getting stuck inside the tile due to the collision code assigning a velocity insufficient to cause your object to exit the tile by the next frame.
Generally what I do is I not only invert the velocity along the normal (e.g. vx -> -vx for hitting the left/right sides of a tile) but I also re-position the object so its 'just touching'. That way I guarantee that it won't collide again next frame. This is especially important if you want the collisions to lose a bit of energy (so you do vx -> -0.9*vx, for example), since if you don't do this you're guaranteed to get 'trapped' in the tile.