Bounding box collision in regards to the top, bottom, left and right of the box
aoxomoxoa

I am working on a brick breaker type game, so it was necessary for me to know not just that the ball collided with the brick but also which side that it collided on.

What i did, is that i essentially create four more bounding boxes ( logically in the code, not actually as a instance of a Hit_box struct ) that are three pixels deep from their respective sides.

Once i know that the ball collided with the brick, i call this function to determine which side it hit ( LEFT, RIGHT, TOP and BOTTOM being members of an enum, the parameters being members of the ball )

#SelectExpand
1int Brick::coll(int ax, int ay, int aw, int ah) 2{ 3 if( ax+aw > x and ax+aw < x+3 ) return LEFT;//problems 4 if( ax < x+w and ax > x+w-3 ) return RIGHT; 5 if( ay+ah > y and ay+ah < y+3 ) return TOP;//problems 6 if( ay < y+h and ay > y+h-3 ) return BOTTOM; 7 return -1; 8}

The problem is this: it never returns LEFT or TOP.

I am guessing it has something to do with the "a[x/y]+a[w/h] > [x/y] and a[x/y]+a[w/h] < [x/y]+3" conditions being logically flawed, but i can't see the problem.

Any help would be appreciated!

Edgar Reynaldo

Just write a rectangle class and give it a contains(int x , int y) method. Then make rectangles for each side and say if (left_rectangle.contains(ballx,bally)) {return LEFT;}

aoxomoxoa

That would help if my ball were a pixel, but that would be a bit difficult to see :)

The resolution to my problem would be, in the best case scenario, a revision of the conditions for the first and third if statement.

If it helps people visualize better, attached is a hitbox map of one of the bricks. If there is overlap on the red areas, it SHOULD return LEFT. If there is overlap on the brown areas, it SHOULD return TOP. If there is overlap on the cyan areas, it returns RIGHT. If there is overlap on the yellow areas, it returns BOTTOM.

And if it makes it more clear:
(ax,ay) = top left corner of ball.
(ax+aw,ay) = top right corner of ball.
(ax,ay+ah) = bottom left corner of ball.
(ax+aw,ay+ah) = bottom left corner of ball.

Gnamra

if( ax+aw > x and ax+aw < x+3 ) return LEFT;//problems

Your checks are very prone to the bullet through paper problem,
if your ball moves fast enough, and in one frame goes further than x + 3 it will not return, that could be the problem.

So, if in one frame it is 1 pixel away from the bounding box, and has a speed of 4 pixels or higher it will fail. One solution is to include the speed of the ball.

Edgar Reynaldo
aoxomoxoa said:

That would help if my ball were a pixel, but that would be a bit difficult to see :)

So then write a rectangle overlap function.

typedef struct Rect {int lx,ty,rx,by,w,h;};

int Overlaps(Rect r1 , Rect r2) {
   if ((r1.lx > r2.rx) || (r1.rx < r2.lx) || (r1.ty > r2.by) || (r1.by < r2.ty)) {return 0;}
   return 1;
}

Now make a rectangle for each side of the brick, and check if the ball overlaps it. And pay attention to what Gnamra said about it moving too fast.

aoxomoxoa
Gnamra said:

if your ball moves fast enough, and in one frame goes further than x + 3 it will not return, that could be the problem.

No,no. Like i said, this only happens when coming from the left or top. I had thought of that initially, though and tried to increase the width of the bounding box to compensate. That didn't work. So i just set a ball going 1pxl right per frame at the left of the block. It still went through.

Since it returns correctly on RIGHT and BOTTOM, i can only assume that it is a problem with the top and left IF statements.

Gnamra

It could also be that your first collision check is incorrect, because if it is only going 1 px per frame then it should work. Perhaps you could post some more code?

aoxomoxoa

Oh good lord. Thank you SO much.

It turns out i had replaced the initial collision detection with tile system, since all of the bricks are of the same width and height. That let me get rid of the time consuming For loops.

Unfortunately, i forgot to account for the bottom and right of the ball when i made the switch.

Sorry about that, I'm still having trouble wrapping my mind around such a (comparatively) large project. :-/

Neil Roy

I recently work on the same type of game. One extra thing to note is that if your bricks are close together (in my game they touch), than the ball can potentially hit more than one brick at a time, you need to check for this.

If the ball collides with the bottom of a brick, than you need to check if it also collided with the side of another adjacent brick...

   _
 _|_|
|_|O

.. as you can see the ball will collide with two bricks here at the same time given the right circumstances.

If you count the time a ball hits this becomes more important. It's really easier than it looks. Here's the code for how I do it:

#SelectExpand
1// checks for collision between ball and blocks 2int collision2(Ball *ball) { 3 // get the sides of the ball 4 int left = (int)ball->get_x(); 5 int right = (int)left + ball->get_width()-1; 6 int top = (int)ball->get_y(); 7 int bottom = top + (int)ball->get_height()-1; 8 9 // the bricks are all between 100 and 300 on the screen 10 // so no need to check further if the ball is outside this range. 11 if (top>=300) return 0; 12 if (bottom<100) return 0; 13 14 int collided = 0; // keep track of the number of bricks hit for scoring 15 int x, y; 16 17 if (ball->get_dy() < 0) 18 y = (top-100)/20; 19 else 20 y = (bottom - 100)/20; 21 22 x = ((int)ball->get_x() + ball->get_width()/2)/40; 23 if ((x>=0 && x<20) && (y>=0 && y<10)) { 24 if (map[y][x]) { 25 map[y][x] = 0; 26 ball->set_dy(-ball->get_dy()); 27 collided++; 28 } 29 } 30 31 if (ball->get_dx() > 0) 32 x = right/40; 33 else 34 x = left/40; 35 36 y = ((int)ball->get_y() + ball->get_height()/2 - 100)/20; 37 if ((x>=0 && x<20) && (y>=0 && y<10)) { 38 if (map[y][x]) { 39 map[y][x] = 0; 40 ball->set_dx(-ball->get_dx()); 41 collided++; 42 } 43 } 44 45 return collided; 46}

I checked both vertical and horizontal collisions just in case it hit a second brick at the same time.

For really easy, basic bouncing box collision detection this works well:

if (bottom1 < top2) return(false);
if (top1 > bottom2) return(false);
if (right1 < left2) return(false);
if (left1 > right2) return(false);

You don't check for overlap, but rather you check if they don't overlap. If the bottom of sprite one is less than the top of sprite two, than a collision between the two is impossible and the function can return false.

You do the four tests above and if any one of them fails, no collision. But if they all pass, than you have a collision. You can then add in more code to find just where that happened. In my own function I then have some code to detect for pixel perfect detection which isn't needed in most cases. It's a nice fast way to check for collisions though.

Specter Phoenix

I've made a pong clone numerous times and every time I forget to check the top and bottom of the paddles to react accordingly. Slow learner. Though this helps me understand collision detection a little more.

Thread #610328. Printed from Allegro.cc