Friday, March 25, 2011

Breakout Week 4: Building a Wall (Part 2)

This is part two of my two part post of building the wall in my Breakout game.

With the wall in place, the next step is to make the ball break the bricks as it hits the wall.  This bring us back to collision detection and response again.

I did quite a bit of reading on this because there are a number of ways you could do it.  The collision detection part can be fairly easy, where you just check if the ball is within the bounds of each brick, but it gets complicated by the fact that once you know a collision occurred, you also need to know which side of the brick the ball hit because it could hit any of the brick's four sides or corners.  You need this in order to know in which direction to bounce the ball after it hits the brick.  There seemed to be a couple common techniques for doing this, well common ones I found in a quick google search at least.

The first technique involved looking at the previous position of the ball and it's position relative to the brick. For example, you create regions based on the edges of the brick and find which region the ball was in before it hit the brick. The regions would like this:





If the ball was in the top, bottom, left or right regions then you assume that was the edge that the ball hit.  If it was in one of the diagonal regions, then it the corner.  The problem with this method is that it doesn't take into account the direction or velocity of the ball. Consider the case of a ball being in one of the diagonal regions but travelling fast enough and at the right angle that by the end of the frame it was going to hit one of the edges, not the corner. This diagram illustrates this:


Because this method ignores the direction and speed of the ball it would detect a corner collision here, even though it is clearly hitting on the bottom edge of the brick. This would give you a lot of false corner collisions which would look weird, since a corner collision results in the ball bouncing back the way it came.

Another technique is to, once you know a ball collided with a brick, do a line intersection test between each edge of the brick and the line created by the previous and current position of the ball. So basically if the ball is travelling up we create a line segment from the center of the top edge of the ball's starting and ending position and see if it it intersects the bottom edge of the brick. Which looks like this:


You would then do that for the x-direction as well and which ever edge the ball intersects is the edge on which you reflect the ball's direction.  If you don't get an intersection then you have hit a corner and you can just reflect back in both x and y directions.  This is the method I actually implemented and it works reasonable well, as you can see in this video:






However, you might notice that occasionally the ball hits what looks like the edge of the brick but bounces back in the opposite direction. This just looks weird and it totally the opposite of what the player would expect.  We can see why this happens though by looking at the algorithm for detecting the edge that was hit.  Basically, to do the line intersection test we need to pick a point on the edge of the ball that is closest to the brick, the central point of that edge makes the most sense, but in doing that we also increase the threshold for detecting a corner collision. Consider this collision, which clearly occurs between the top edge of the ball and the bottom of the brick (note that Ball A is the starting position and Ball B is the end position of the ball):



The line we would test for a collision on the bottom edge of the brick would run from the center of the top edge of Ball A to the same point of Ball B, which would look like this:


But that is going to miss the bottom edge,  which is not what we want.  If we say, ok lets also check the center of the left edge, then sure that will intersect but not if the ball is on the other side, so you'd have to check right side as well.  So it becomes apparent that what you really need is the Axis-Aligned Bounding Box Sweep Test that I talked about a couple of weeks ago (the circle is complete).  With the sweep test you can get the exact position at which the collision occurred and then determine which edge was hit by doing the region test based on the collision position. This will be much more accurate and corner hits will only occur when the ball actually does hit the corner, which would be pretty rare. It will also be easier to handle collisions on multiple bricks.

So my next task is to convert my existing code, that uses the line intersection method, to the AABB sweep test.  I'll do that this week.  I haven't included the code from the intersection test in this post because I will pretty much be replacing it next week so it didn't see worthwhile explaining the guts of it.  If anyone is interested, it is available up on GitHub with it's own tag at week4p2. So you can see the method I was using detail if you want.

Also, if anyone is interested in the details of the line intersection code, I based mine on this excellent example from Third Party Ninjas.

'til next week...

No comments: