2D Collision Detection and Response?

Whether you're a newbie or an experienced programmer, any questions, help, or just talk of any language will be welcomed here.

Moderator: Coders of Rage

User avatar
dandymcgee
ES Beta Backer
ES Beta Backer
Posts: 4709
Joined: Tue Apr 29, 2008 3:24 pm
Current Project: https://github.com/dbechrd/RicoTech
Favorite Gaming Platforms: NES, Sega Genesis, PS2, PC
Programming Language of Choice: C
Location: San Francisco
Contact:

2D Collision Detection and Response?

Post by dandymcgee »

So I've decided my design skills are incredibly bad, and I'm lacking some very important fundamentals. Therefore I've also decided to make a Breakout clone.

Implementation details:
Bricks are represented as a vector of Objects. When the ball collides with a brick, it is removed from the vector. This part works fine at the moment, the question comes into play when figuring out changes in the ball's velocity due to a collision.

Right now if the ball hits a brick or the paddle I simply reverse the y velocity. This works fine if it hits the top of the paddle or the bottom of a brick.. but what if it hits the side? Obviously I would negate the x velocity as opposed to the y, but how would I determine with which side of a brick the ball has collided?

This is what I've written to start:

Code: Select all

if( (ball.x + ball.w + ball.xVel > brick.x) && (ball.x + ball.xVel < brick.x + brick.w) && (ball.y + ball.h + ball.yVel > brick.y) && (ball.y + ball.yVel < brick.y + brick.h))
{
     delete (*bricksIter);
     bricks.erase( bricksIter );
     ball->yVel = -ball->yVel;
}
I would imagine determining the collision side involves splitting this into at least four separate conditions, but I figured I would inquire before writing a butt load of unnecessary if statements.

Any ideas/suggestions (yes I realize this is elementary rectangular collision...)? :lol:
Falco Girgis wrote:It is imperative that I can broadcast my narcissistic commit strings to the Twitter! Tweet Tweet, bitches! :twisted:
User avatar
Falco Girgis
Elysian Shadows Team
Elysian Shadows Team
Posts: 10294
Joined: Thu May 20, 2004 2:04 pm
Current Project: Elysian Shadows
Favorite Gaming Platforms: Dreamcast, SNES, NES
Programming Language of Choice: C/++
Location: Studio Vorbis, AL
Contact:

Re: 2D Collision Detection and Response?

Post by Falco Girgis »

You need to find the "axis" of minimal overlap.

Imagine two rectangles colliding. You have four possible edges/faces on either rectangle that the collision could have occurred on. If you would like to determine which edge has "collided" with a rectangle, it will always be the edge that is penetrating the other rectangle minimally.

If your axis of minimal overlap for your "ball" is on either the top or bottom, negate your y velocity.
If your axis of minimal overlap for your "ball" is on either the left or right side, negate your x velocity.

Consider this code (I'm pulling this out of my ass, so it is untested) within your collision resolution:

Code: Select all

//Determine overlap for each axis
xOverlap = rect1.x+0.5*rect1.width - rect2.x + 0x5*rect2.width;
yOverlap = rect1.y+0.5*rect1.height - rect2.y+0.5*rect2.height;

//Test for axis of minimal overlap
if(abs(xOverlap) < abs(yOverlap)) {
    rect1.xvel = -rect1.xvel;
}
else rect1.yvel = -rect1.yvel;
User avatar
dandymcgee
ES Beta Backer
ES Beta Backer
Posts: 4709
Joined: Tue Apr 29, 2008 3:24 pm
Current Project: https://github.com/dbechrd/RicoTech
Favorite Gaming Platforms: NES, Sega Genesis, PS2, PC
Programming Language of Choice: C
Location: San Francisco
Contact:

Re: 2D Collision Detection and Response?

Post by dandymcgee »

GyroVorbis wrote:You need to find the "axis" of minimal overlap.

Imagine two rectangles colliding. You have four possible edges/faces on either rectangle that the collision could have occurred on. If you would like to determine which edge has "collided" with a rectangle, it will always be the edge that is penetrating the other rectangle minimally.

If your axis of minimal overlap for your "ball" is on either the top or bottom, negate your y velocity.
If your axis of minimal overlap for your "ball" is on either the left or right side, negate your x velocity.

Consider this code (I'm pulling this out of my ass, so it is untested) within your collision resolution:

Code: Select all

//Determine overlap for each axis
xOverlap = rect1.x+0.5*rect1.width - rect2.x + 0x5*rect2.width;
yOverlap = rect1.y+0.5*rect1.height - rect2.y+0.5*rect2.height;

//Test for axis of minimal overlap
if(abs(xOverlap) < abs(yOverlap)) {
    rect1.xvel = -rect1.xvel;
}
else rect1.yvel = -rect1.yvel;
Brilliant! :) I knew you would have something much better that whatever I would have conceived. The only place where this becomes kind of personalized is where xOverlap==yOverlap, but I'll probably just reverse both velocities if someone gets THAT lucky. ;)

Edit:
xOverlap = rect1.x+0.5*rect1.width - rect2.x + 0x5*rect2.width;
"Wtf.. it's not working."
Lmao. :lol:
Falco Girgis wrote:It is imperative that I can broadcast my narcissistic commit strings to the Twitter! Tweet Tweet, bitches! :twisted:
User avatar
davidthefat
Chaos Rift Maniac
Chaos Rift Maniac
Posts: 529
Joined: Mon Nov 10, 2008 3:51 pm
Current Project: Fully Autonomous Robot
Favorite Gaming Platforms: PS3
Programming Language of Choice: C++
Location: California
Contact:

Re: 2D Collision Detection and Response?

Post by davidthefat »

User avatar
dandymcgee
ES Beta Backer
ES Beta Backer
Posts: 4709
Joined: Tue Apr 29, 2008 3:24 pm
Current Project: https://github.com/dbechrd/RicoTech
Favorite Gaming Platforms: NES, Sega Genesis, PS2, PC
Programming Language of Choice: C
Location: San Francisco
Contact:

Re: 2D Collision Detection and Response?

Post by dandymcgee »

That's exactly what I was doing before, doesn't tell you which side was collided with. ;)

Falco, I'm having an issue with the overlap code:
error.PNG
error.PNG (1.58 KiB) Viewed 4796 times
Any more ideas?
Falco Girgis wrote:It is imperative that I can broadcast my narcissistic commit strings to the Twitter! Tweet Tweet, bitches! :twisted:
User avatar
Falco Girgis
Elysian Shadows Team
Elysian Shadows Team
Posts: 10294
Joined: Thu May 20, 2004 2:04 pm
Current Project: Elysian Shadows
Favorite Gaming Platforms: Dreamcast, SNES, NES
Programming Language of Choice: C/++
Location: Studio Vorbis, AL
Contact:

Re: 2D Collision Detection and Response?

Post by Falco Girgis »

dandymcgee wrote:Brilliant! :) I knew you would have something much better that whatever I would have conceived. The only place where this becomes kind of personalized is where xOverlap==yOverlap, but I'll probably just reverse both velocities if someone gets THAT lucky.
Don't bother. If somebody were to get that lucky, the physics would be completely believable in either direction. Hence I didn't address that case.

Oops, my previous algorithm wasn't quite right. Sue me. I pulled a new and improved algorithm out of my ass. This should be a complete collision check for you. The overlaps on the axes should have been calculated based on the difference between the distance of the two centers and the maximal distance that they can be without colliding.

Make sure your x,y is at the center of the rectangle (not top left).

Code: Select all

bool Collision(Rect rect1, Rect rect2) {
//Determine overlap for each axis
float xDist = abs(rect1.x - rect2.x);
float yDist = abs(rect1.y - rect2.y);

//minimal amount of distance that the two can be apart and not collide
float xMinDist = rect1.width + rect2.width;
float yMinDist = rect1.height + rect2.height;

if(xDist >= xMinDist || yDist >= yMinDist) return false; //neither axis is colliding

float xOverlap = xDist - xMinDist;
float yOverlap = yDist - yMinDist;

if(xOverlap < yOverlap) xvel = -xvel;
else yvel = -yvel;

return true;
}
User avatar
Falco Girgis
Elysian Shadows Team
Elysian Shadows Team
Posts: 10294
Joined: Thu May 20, 2004 2:04 pm
Current Project: Elysian Shadows
Favorite Gaming Platforms: Dreamcast, SNES, NES
Programming Language of Choice: C/++
Location: Studio Vorbis, AL
Contact:

Re: 2D Collision Detection and Response?

Post by Falco Girgis »

You better let me know if that didn't work. Otherwise I will assume my untested code was flawless.
User avatar
dandymcgee
ES Beta Backer
ES Beta Backer
Posts: 4709
Joined: Tue Apr 29, 2008 3:24 pm
Current Project: https://github.com/dbechrd/RicoTech
Favorite Gaming Platforms: NES, Sega Genesis, PS2, PC
Programming Language of Choice: C
Location: San Francisco
Contact:

Re: 2D Collision Detection and Response?

Post by dandymcgee »

GyroVorbis wrote:You better let me know if that didn't work. Otherwise I will assume my untested code was flawless.
Haven't had time to implement it yet, I was busy all day yesterday. I'll do it in a bit and let ya know. ;)
Falco Girgis wrote:It is imperative that I can broadcast my narcissistic commit strings to the Twitter! Tweet Tweet, bitches! :twisted:
User avatar
dandymcgee
ES Beta Backer
ES Beta Backer
Posts: 4709
Joined: Tue Apr 29, 2008 3:24 pm
Current Project: https://github.com/dbechrd/RicoTech
Favorite Gaming Platforms: NES, Sega Genesis, PS2, PC
Programming Language of Choice: C
Location: San Francisco
Contact:

Re: 2D Collision Detection and Response?

Post by dandymcgee »

Gah, finally figured out why it wasn't working right.

I was calling:

Code: Select all

delete (*bricksIter);
bricks.erase( bricksIter );
BEFORE testing for collisions (where I referenced (*bricksIter) multiple times). :|

Also, note to self: abs() casts doubles to ints. :roll:

Thanks Falco.
Falco Girgis wrote:It is imperative that I can broadcast my narcissistic commit strings to the Twitter! Tweet Tweet, bitches! :twisted:
User avatar
Falco Girgis
Elysian Shadows Team
Elysian Shadows Team
Posts: 10294
Joined: Thu May 20, 2004 2:04 pm
Current Project: Elysian Shadows
Favorite Gaming Platforms: Dreamcast, SNES, NES
Programming Language of Choice: C/++
Location: Studio Vorbis, AL
Contact:

Re: 2D Collision Detection and Response?

Post by Falco Girgis »

dandymcgee wrote:Gah, finally figured out why it wasn't working right.

I was calling:

Code: Select all

delete (*bricksIter);
bricks.erase( bricksIter );
BEFORE testing for collisions (where I referenced (*bricksIter) multiple times). :|

Also, note to self: abs() casts doubles to ints. :roll:

Thanks Falco.
No problem at all. The same geometric interpretation of the problem can be applied to oriented bounding boxes as well (where they are both rotated). You basically just applied the separating axis theorem on axis-aligned bounding boxes.
User avatar
dandymcgee
ES Beta Backer
ES Beta Backer
Posts: 4709
Joined: Tue Apr 29, 2008 3:24 pm
Current Project: https://github.com/dbechrd/RicoTech
Favorite Gaming Platforms: NES, Sega Genesis, PS2, PC
Programming Language of Choice: C
Location: San Francisco
Contact:

Re: 2D Collision Detection and Response?

Post by dandymcgee »

GyroVorbis wrote:No problem at all. The same geometric interpretation of the problem can be applied to oriented bounding boxes as well (where they are both rotated). You basically just applied the separating axis theorem on axis-aligned bounding boxes.
Oh, sweet. Now I'm one step closer to understanding separating axis. ;)
Falco Girgis wrote:It is imperative that I can broadcast my narcissistic commit strings to the Twitter! Tweet Tweet, bitches! :twisted:
User avatar
DeltaFolee
Chaos Rift Newbie
Chaos Rift Newbie
Posts: 22
Joined: Sun Jun 07, 2009 3:23 pm
Favorite Gaming Platforms: PC, GameCube, PS4
Programming Language of Choice: C++
Location: University Park, PA
Contact:

Re: 2D Collision Detection and Response?

Post by DeltaFolee »

GyroVorbis wrote:
dandymcgee wrote:Brilliant! :) I knew you would have something much better that whatever I would have conceived. The only place where this becomes kind of personalized is where xOverlap==yOverlap, but I'll probably just reverse both velocities if someone gets THAT lucky.
Don't bother. If somebody were to get that lucky, the physics would be completely believable in either direction. Hence I didn't address that case.

Oops, my previous algorithm wasn't quite right. Sue me. I pulled a new and improved algorithm out of my ass. This should be a complete collision check for you. The overlaps on the axes should have been calculated based on the difference between the distance of the two centers and the maximal distance that they can be without colliding.

Make sure your x,y is at the center of the rectangle (not top left).

Code: Select all

bool Collision(Rect rect1, Rect rect2) {
//Determine overlap for each axis
float xDist = abs(rect1.x - rect2.x);
float yDist = abs(rect1.y - rect2.y);

//minimal amount of distance that the two can be apart and not collide
float xMinDist = rect1.width + rect2.width;
float yMinDist = rect1.height + rect2.height;

if(xDist >= xMinDist || yDist >= yMinDist) return false; //neither axis is colliding

float xOverlap = xDist - xMinDist;
float yOverlap = yDist - yMinDist;

if(xOverlap < yOverlap) xvel = -xvel;
else yvel = -yvel;

return true;
}
The part of that algorithm where you calculated the minimum distance allowed between the two boxes doesn't make any sense to me, I thought it would just be the larger of the two widths/heights would be the minimal distance. Could someone please explain this for me?
Last edited by DeltaFolee on Fri Jan 01, 2010 7:57 pm, edited 1 time in total.
If only I could be so grossly incandescent
User avatar
Bakkon
Chaos Rift Junior
Chaos Rift Junior
Posts: 384
Joined: Wed May 20, 2009 2:38 pm
Programming Language of Choice: C++
Location: Indiana

Re: 2D Collision Detection and Response?

Post by Bakkon »

dandymcgee wrote: Also, note to self: abs() casts doubles to ints. :roll:
This is way late and you probably know this by now, but fabs() returns a float, double, or long depending on what's passed in.
User avatar
dandymcgee
ES Beta Backer
ES Beta Backer
Posts: 4709
Joined: Tue Apr 29, 2008 3:24 pm
Current Project: https://github.com/dbechrd/RicoTech
Favorite Gaming Platforms: NES, Sega Genesis, PS2, PC
Programming Language of Choice: C
Location: San Francisco
Contact:

Re: 2D Collision Detection and Response?

Post by dandymcgee »

Bakkon wrote:
dandymcgee wrote: Also, note to self: abs() casts doubles to ints. :roll:
This is way late and you probably know this by now, but fabs() returns a float, double, or long depending on what's passed in.
Not late at all, I most certainly appreciate that information! Will keep that in mind for 2010. ;)
Falco Girgis wrote:It is imperative that I can broadcast my narcissistic commit strings to the Twitter! Tweet Tweet, bitches! :twisted:
User avatar
koolgraph
Chaos Rift Newbie
Chaos Rift Newbie
Posts: 18
Joined: Thu Feb 17, 2011 1:49 am
Current Project: A-RPG (MMO ?) Engine
Favorite Gaming Platforms: SNES, GameBoy, PS3, PC
Programming Language of Choice: C++/#

Re: 2D Collision Detection and Response?

Post by koolgraph »

The Collision Detection works great, except when it collide at a high speed. Exemple

Speed = 10 Char by tick
X = the Player
OO = Some kind of brick/wall

First Tick

Code: Select all

--------OOOO
-X------OOOO
--------OOOO
Enverything's fine

Next Tick

Code: Select all

--------OOOO
--------OOXO
--------OOOO
Now it says the YDistance is less than X distance, so pull back top, but the collision did happened on the side. How to fix this ?

I should say, how to take into account the Velocity in the Collision Response ?
Post Reply