Determine what side of rectangle is hit by ball

Started by
15 comments, last by Dovakhiin 7 years, 11 months ago

I am trying to make a breakout game using Monogame. There is a bug on the game where when the ball hits the side of the paddle it kept on shaking.

ball_paddle.jpg

So I was wondering is there a good way to determine which side of the paddle has been hit? I am using the intersect method of the rectangle.

I thought of getting the distance of the two lines but Its not working for me. Any ideas? TY

Advertisement

Compare the x value of the ball to the min/max x values of the rectangle.

Hello to all my stalkers.

If you are using the intersects method, make sure you know how simple collision detection works.

http://lazyfoo.net/tutorials/SDL/27_collision_detection/index.php

(the tutorial uses SDL, but would be fine with any other library)

Since, the method given my Lactose would be quite obvious after understanding how it works.

Also, you can write your own collision detection function instead, which also returns some extra information about the relative position of the object which collides.

If you are using the intersects method, make sure you know how simple collision detection works.

http://lazyfoo.net/tutorials/SDL/27_collision_detection/index.php

(the tutorial uses SDL, but would be fine with any other library)

Since, the method given my Lactose would be quite obvious after understanding how it works.

Also, you can write your own collision detection function instead, which also returns some extra information about the relative position of the object which collides.

Oh god the blue color hurts my eye.. But thanks.. ill see if i can understand it

I highly suggest that you pick up a copy of Real-Time Collision Detection by Christer Ericson. These sorts of simple geometric intersection tests are super common in games in both 2D and 3D and you owe it to yourself to learn the general solutions (and to have a handy reference laying around when you need to look up a specific algorithm).

Sean Middleditch – Game Systems Engineer – Join my team!

I highly suggest that you pick up a copy of Real-Time Collision Detection by Christer Ericson. These sorts of simple geometric intersection tests are super common in games in both 2D and 3D and you owe it to yourself to learn the general solutions (and to have a handy reference laying around when you need to look up a specific algorithm).

Cool thanks for the tip

Compare the x value of the ball to the min/max x values of the rectangle.

Hmmm this is quite hard.


if (Position.X + Width > catcher.Position.X && Position.Y + Height > catcher.Position.Y + (catcher.Height / 2))
                 {
                     Debug.WriteLine("hit at side");
                     Position = new Vector2(catcher.Position.X - Width, Position.Y);
                     Direction = new Vector2(-1, -Direction.Y);
                     
                     //Speed = 0f;
                 }
                 else if(Position.X < catcher.Position.X + Width && Position.Y + Height > catcher.Position.Y + (catcher.Height / 2))
                 {
                     Debug.WriteLine("hit at right side");
                     Position = new Vector2(catcher.Position.X + catcher.Width, Position.Y);
                     Direction = new Vector2(-1, -Direction.Y);
                     Speed = 0f;

                 }

Both will return true cause the ball position X will always return true even if it hit the left side or the right side. Its hard to differentiate them accordingly. Doing that Ill be able to bounce the ball as well base on where it hits. Any tips? ty

It would help if you explain what your "this" object is, and what "catcher" is.

Also, what tests are you doing there (like "this against left top side of paddle" or so).

Right now it is hard to understand which case you're doing where.

Anyway, this looks wrong to me, position of one object adding the width of another object is not likely to give a useful result.


catcher.Position.X + Width

I would suggest to add a few variables to make things easier to read. (Not sure what language you use, so I just invent my own syntax, but it's easy to translate to whatever you use.)


catcher_left = catcher.Position.X
catcher_right = catcher.Position.X + catcher.Width
// and so on.

// A condition then becomes
if (self_right > catcher_left && self_bottom > catcher_center_y) { ... }

Making such variables also helps in avoiding errors like adding wrong values together, since omitting "catcher" in the new variables is likely to get noticed.

If these variables occur a lot, you can even add them as functions to your object:


def right():  // Your function looks different, but I hope you understand what I try to say
    return Position.X + Width;

which then makes it possible to write


catcher.right()

everywhere that you need it.

The underlying problem here is that a single state in time is used to determine which edge of the block was hit first by the ball. But "first" and "second" is an ordering criteria in time. The higher the relative speed (i.e. the delta movement distance since last collision determination) the less reliably can the penetration depth be used.

I don't know whether a trick exists to do the job in a clever way. In general, if you determined that the objects do collide now, you have to rollback time, so shift the objects back to their respective positions to where collision just began, and then check for the edge that was involved (considering the case that actually the corner was hit).

I assume the following situation: One object, the ball, can be described as a moving circle. Its position is given w.r.t. its center, and its radius is known. The other object, the paddle, can be described as a moving rectangular, axis aligned block. It position is given w.r.t. its center, and its width and height are known. During a single time step, both movements can be assumed to be linear (straight and not accelerated).

Let t1 denote the current time and t0 the time valid at the previous time step. Let pb(t1) and pb(t0) the corresponding positions of the ball. Let pp(t1) and pp(t0) the corresponding positions of the paddle. By time normalization

k := ( t - t0 ) / ( t1 - t0 ) so that 0 <= k <= 1 within the last time step,

we can say

pb(k) := pb(t0) + k * ( pb(t1) - pb(t0) )

pp(k) := pp(t0) + k * ( pp(t1) - pp(t0) )

The task is now to compute that k where the first collision takes place. A little trick here is to separate both spatial axes. That reduces the problem, because now we have 3 collisions between a point and a line segment in 1D. To do this we first define the positions of the 4 corners of the block using its width w and height h as:

ctl(k) := pp(k) + [ -w/2 +h/2 ]T

ctr(k) := pp(k) + [ +w/2 +h/2 ]T

cbl(k) := pp(k) + [ -w/2 -h/2 ]T

cbr(k) := pp(k) + [ +w/2 -h/2 ]T

Now, for example let's say that from the above we calculate the left edge of the block as line segment in 2D

el(k,f) := cbl(k) + f * ( ctl(k) - cbl(k) ) = cbl(k) + f * [ 0 h ]T w/ 0 <= f <= 1

so that its x component is just

xl(k,f) = xbl(k)

For the same k, the x component of the point of the ball that collides first is the rightmost point on the circle, i.e.

xcr(k) = xb(k) + R

where R denotes the radius. Now compute the k' by solving

xcr(k') == xl(k',f)

and check for condition

0 <= k' <= 1

If the condition is violated then the edge is not hit and hence not considered further. Otherwise compute f' by using k' from

xl(k',f') = ...

and check the condition

0 <= f' <= 1

If the condition is violated, then the left edge is not the one touched first and hence not considered further. Although … if you want to check for collision with the corners, you should use a less strict condition, i.e. with a small positive tolerance value d, something like

0 - d <= f' <= 1 + d

Do the above analogously for the right and top edges, too. (I assume that the bottom edge plays no role.) Then the overall result is a set of 0, 1, 2, or 3 values of k'. If the set is empty then there is no collision. Otherwise the source of the minimal k' in the set tells you which edge was hit first. If the minimal k' and the next greater k' are very close (compared to a defined limit), then the ball should be considered to hit those corner of the paddle to which the source edges of both k' belong to.

Oh dear! I should stop writing such long posts … well, hopefully it helps to at least understand the problem behind the problem ;) Notice that the above resulted from mental work, so check twice for correctness if you actually want to use it.

It would help if you explain what your "this" object is, and what "catcher" is.

Also, what tests are you doing there (like "this against left top side of paddle" or so).

Right now it is hard to understand which case you're doing where.

Anyway, this looks wrong to me, position of one object adding the width of another object is not likely to give a useful result.


catcher.Position.X + Width

I would suggest to add a few variables to make things easier to read. (Not sure what language you use, so I just invent my own syntax, but it's easy to translate to whatever you use.)


catcher_left = catcher.Position.X
catcher_right = catcher.Position.X + catcher.Width
// and so on.

// A condition then becomes
if (self_right > catcher_left && self_bottom > catcher_center_y) { ... }

Making such variables also helps in avoiding errors like adding wrong values together, since omitting "catcher" in the new variables is likely to get noticed.

If these variables occur a lot, you can even add them as functions to your object:


def right():  // Your function looks different, but I hope you understand what I try to say
    return Position.X + Width;

which then makes it possible to write


catcher.right()

everywhere that you need it.

Hi, Sorry for confusion. Actually I always use functions, but sometimes when my code got some bug on it, I just put it together on for example my main function and when its fix, Ill move it to a function.

I am using XNA/c#. What I had in mind is that when the ball hits the left side it will bounce the object downwards(not upwards) because the ball wasnt at the top of the paddle. Same thing happen in the right side.

I am coding it on the Ball class. the this represents the ball itself and the catcher is the paddle.


// If the right side of the ball hit the left side of the paddle and the height of the ball is greater than the paddle's y position
               if (Position.X + Width > catcher.Position.X    && Position.Y + Height > catcher.Position.Y + (catcher.Height / 2))
               {
                   Debug.WriteLine("hit at side");
                   Position = new Vector2(catcher.Position.X - Width, Position.Y); // place the ball on the side where it hits(left side of paddle) before bouncing the ball downwards
                   Direction = new Vector2(-1, -Direction.Y);  // bounce the ball downwards
                   
                   //Speed = 0f;
               }

               // If the Left side of the ball hit the Right side of the paddle and the height of the ball is greater than the paddle's y position
               if (Position.X < catcher.Position.X + catcher.Width  / 2 && Position.Y + Height > catcher.Position.Y + (catcher.Height / 2))
               {
                   Debug.WriteLine("hit at right side");
                   Position = new Vector2(catcher.Position.X + catcher.Width, Position.Y); // place the ball on the side where it hits(the right side of paddle) before bouncing the ball downwards
                   Direction = new Vector2(-1, -Direction.Y); // bounce the ball downwards
                   Speed = 0f;

               }

I added some comments to better understand it. The Speed is set to 0 as a debugging thing. I just want to make sure that when it hits the side, It will stay there. Although I fix the shaking of the ball, the problem now is that bot If's statement returns true. So an unusual thing happend. When I execute the program, both debug messages show on my output command line so when the ball hit the left-side, the ball will position itself to right and vice versa. This happens because both If's condition return true. Also keep in mind that the origin(0,0) is on the top-left corner of the screen. The two If's is inside an if statement but this if check the rectangle to rectangle collision by using the intersect method. Basically it looks something like this


if(rectangle.intersect(ball)){
    Position += (this.Direction * (float)dt.ElapsedGameTime.TotalSeconds) * Speed; // update the ball position
    If(leftsidePaddleHit()){
       // bounce the ball going downwards from the left side of paddle
   }

if(rightSidePaddleHit()){
 // bounce the ball going downwards from the right side of paddle
   }
}

I hope this will make the code easier to understand.

This topic is closed to new replies.

Advertisement