Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

After this question, I need some more help.

How can I find out which side of a rectangle a collision came from and react accordingly?

rectangles being collided with from all sides

The blue arrows are the paths that some circular objects would follow if before and after colliding with the box.

How can I calculate this?

share|improve this question
add comment

5 Answers 5

up vote 8 down vote accepted

Since this is based on your other question I'll give a solution for when the rectangle is axis-aligned.

First, you build up you current object's rectangle with the following values:

int boxLeft = box.X;
int boxRight = boxLeft + box.Width;
int boxTop = box.Y;
int boxBottom = boxTop + box.Height;

Next, you must have the old object's position (which you can store on each object or simply pass to a function) to create the old object's rectangle (when it was not colliding):

int oldBoxLeft = box.OldX;
int oldBoxRight = oldBoxLeft + box.Width;
int oldBoxTop = box.OldY;
int oldBoxBottom = oldBoxTop + box.Height;

Now, to know where the collision was from, you must find the side where the old position was not in the collision area and where its new position is. Because, when you think of it, this is what happens when you collide: a side that was not colliding enters an other rectangle.

Here is how you could do so (these functions assume that there is a collision. They should not be called if there is no collision):

 bool collidedFromLeft(Object otherObj)
{
    return oldBoxRight < otherObj.Left && // was not colliding
           boxRight >= otherObj.Left;
}

Rince and repeat.

bool collidedFromRight(Object otherObj)
{
    return oldBoxLeft >= otherObj.Right && // was not colliding
           boxLeft < otherObj.Right;
}

bool collidedFromTop(Object otherObj)
{
    return oldBoxBottom < otherObj.Top && // was not colliding
           boxBottom >= otherObj.Top;
}

bool collidedFromBottom(Object otherObj)
{
    return oldBoxTop >= otherObj.Bottom && // was not colliding
           boxTop < otherObj.Bottom;
}

Now, for the actual use with the collision response from the other question:

if (collidedFromTop(otherObj) || collidedFromBottom(otherObj))
    obj.Velocity.Y = -obj.Velocity.Y;
if (collidedFromLeft(otherObj) || collidedFromRight(otherObj))
    obj.Velocity.X = -obj.Velocity.X;

Again, this may not be the best solution but that's the way I usually go for collision detection.

share|improve this answer
    
Once again, you were right! ;D Thanks... (send me more postcards of your board next time... ^___^) –  NemoStein Jun 20 '11 at 20:50
    
Ahhh sadly I didn't know what I could have used it for.. maybe next time! –  Jesse Emond Jun 21 '11 at 1:31
add comment

I usually do it every frame by translating the collision box by one pixel in each direction in turn and see which ones result in collision.

If you want to do it "properly" and with rotated collision shapes or arbitrary polygons, I suggest reading up on the Separating Axis Theorem. Metanet software (the folks who made the N game) for instance have an awesome dev article on SAT. They also discuss the physics involved.

share|improve this answer
add comment

As the question is partially identical to this question, I will re-use some parts of my answer in order to try answering your question.


Lets define a context and some variables to make the following explanation more understandable. The representation form that we will use here probably doesn't fit the form of your own datas but it should be simpler to understand this way (indeed you can use the following methods using others kind of representations once you understand the principle)

So, we will consider an Axis Aligned Bounding Box (or an Oriented Bounded Box) and a moving Entity.

  • The Bounding Box is composed of 4 sides, and we will define each one as:
    Side1 = [x1,y1,x2,y2] (two points [x1,y1] and [x2,y2])

  • The moving Entity is defined as a velocity vector (position+speed):
    a position [posX, posY] and a speed [speedX, speedY].


You can determine which side of an AABB/OBB is hit by a vector using the following method:

  • 1/ Find the intersection points between the infinite lines passing though the four sides of the AABB and the infinite line passing through the entity position (pre-collision) which use the entity speed vector as slop. (You can either find a collision point or an undefined number, which corresponds to parallels or overlapping lines)

  • 2/ Once you know the intersection points (if they exist) you can search for those that are into the segment bounds.

  • 3/ Finally, if there is still multiple points on the list (the velocity vector can pass through multiple sides), you can search for the closest point from the entity origin using the magnitudes of the vector from the intersection to the entity origin.

Then you can determine the angle of collision using a simple dot product.

  • 4/ Find the angle between of collision using a dot product of the entity (probably a ball?) vector with the hit side vector.

----------

More details:

  • 1/ Find intersections

    • a / Determine the infinite lines ( Ax + Bx = D ) using their parametric forms ( P(t) = Po + tD ).

      Point origin: Po = [posX, posY]
      Direction vector: D = [speedX, speedY]

      A = D.y = speedY
      B = -D.x = -speedX
      D = (Po.x * D.y) - (Po.y * D.x) = (posX*speedY) - (posY*speedX)

      Ax + By = D <====> (speedY*x) + (-speedX*y) = (posX*speedY) - (posY*speedX)

      I used the entity point values to illustrate the method, but this is exactly the same method to determine the 4 side infinite lines of the bounding box (Use Po = [x1,y1] and D = [ x2-x1 ; y2-y1 ] instead).

    • b / Next, to find the intersection of two infinite lines we can solve the following system:

      A1x + B1x = D1 <== Line passing through the entity point with the speed vector as slop.
      A2x + B2x = D2 <== One of the lines passing through the AABB sides.

      which yields the following coordinates for the interception:

      Interception x = ((B2*D1) - (B1*D2)) / ((A1*B2) - (A2*B1))
      Interception y = ((A1*D2) - (A2*D1)) / ((A1*B2) - (A2*B1))

      If the denominator ((A1*B2) - (A2*B1)) equals zero, then the both lines are parallels or overlapping, otherwise you should find an intersection.

  • 2/ Test for the segment bounds. As this is straightforward to verify, there is no needs for more details.

  • 3/ Search for the closest point. If there is still multiple points on the list, we can find which side is the closest to the entity origin point.

    • a / Determine the vector going from the intersection point to the entity origin point

      V = Po - Int = [Po.x - Int.x ; Po.y - Int.y ]

    • b / Compute the vector magnitude

      ||V|| = sqrt( V.x² + V.y² )

    • c / find the smallest one.
  • 4/ Now that you know which side will be hit, you can determine the angle using a dot product.

    • a / Let S = [ x2-x1 ; y2-y1 ] be the side vector that will be hit and E = [ speedX ; speedY ] be the entity velocity vector.

      Using the vector dot product rule we know that

      S·E = Sx*Ex + Sy*Ey
      and
      S·E = ||S|| ||E|| cos θ

      So we can determine θ by manipulating a bit this equation...

      cos θ = (S·E) / (||S|| ||E||)

      θ = acos( (S·E) / (||S|| ||E||) )

      with

      S·E = Sx * Ex + Sy * Ey
      ||S|| = sqrt( Sx² + Sy² )
      ||E|| = sqrt( Ex² + Ey² )


Note: As I said in the other question thread, this is probably not the most efficient nor the most simple way to do it, this is just what's come into mind, and some part of the maths could maybe help.

I didn't verified with a concrete OBB example (I did with an AABB) but it should works too.

share|improve this answer
add comment

One way would be to rotate the world around your rectangle. "The world" in this case is only the objects you care about: the rectangle, and the ball. You rotate the rectangle around its center until its bounds are aligned with the x-/y-axes, then you rotate the ball by the same amount.

The important point here is that you rotate the ball around the rectangle's center, not its own.

Then you can easily test for collision just like you would with any other non-rotated rectangle.


Another option is to treat the rectangle as four distinct line-segments, and test for collision with each of them separately. This allows you to test for collision and figure out which side was collided at the same time.

share|improve this answer
add comment

I used fixed angles in my calculations, but this should help you some

void Bullet::Ricochet(C_Rect *r)
{
    C_Line Line;
    //the next two lines are because I detected 
    // a collision in my main loop so I need to take a step back.

    x = x + ceil(speed * ((double)fcos(itofix(angle)) / 65536));
    y = y + ceil(speed * ((double)fsin(itofix(angle)) / 65536));
    C_Point Prev(x,y);

    //the following checks our position to all the lines will give us
    // an answer which line we will hit due to no lines
    // with angles > 90 lines of a rect always shield the other lines.

    Line = r->Get_Closest_Line(Prev);    
    int langle = 0;
    if(!Line.Is_Horizontal())   //we need to rotate the line to a horizontal position
    {
        langle = Line.Get_Point1().Find_Fixed_Angle(Line.Get_Point2());
        angle = angle - langle;  //to give us the new angle of approach
    }
    //at this point the line is horizontal and the bullet is ready to be fixed.
    angle = 256 - angle;
    angle += langle;
}
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.