I've not really contributed anything to the forum yet so I guessed it's time.
Introduction:
So many people are still asking how this is done past the basics and I've seen a lot of talk about collision on this forum but not any "actual tutorials" so enjoy.
So I hope this tutorial will help you understand it better, I'll be going through everything I know of collision detection starting from basic and moving onto more advanced stuff.
Also I will be using SDL and OpenGL with C++ but I'll try to keep that on a different level of abstraction.
Starting From Basics
The fastest and most simple way of detecting collision is simply saying "Is this and this colliding? Y/N".
Code: Select all
bool Eternal::IsCollision(Rectangle *a, Rectangle *b) {
if ( a->x + a->w > b->x
&& a->x < b->x + b->w
&& a->y + a->h > b->y
&& a->y < b->y + b->h ) {
return true;
}
}
None Collision:
Collison detection AND response
What's collision detection without response? We need to make objects solid so we aren't simply informing the player that he's walking through a brick wall.
We could simply do this:
Code: Select all
if ( IsCollision(A,B) ) {
if ( KeyLeft() ) {
A.x++;
}
else if ( KeyRight() ) {
A.x--;
}
if ( KeyUp() ) {
A.y++;
}
else if ( KeyDown() ) {
A.y--;
}
}
The work-around is to calculate the overlap of each axis and figure out which one to act upon.
Code: Select all
bool IsCollision(Rectangle *a, Rectangle *b, Vector2 &normal) {
/* Since the vector is a pointer then it could have been
intialized to any value so we need to make sure we initalize
the vectors X and Y to zero */
normal.x = 0;
normal.y = 0;
// The distance between the two objects
Vector2 Distance;
// The absDistance between the objects
Vector2 absDistance;
float XMagnitute;
float YMagnitute;
// Calculate the distance between A and B
Distance.x = ( ( b->x ) - ( a->x ) );
Distance.y = ( ( b->y ) - ( a->y ) );
// Combine both rectangles and half the returned value
float XAdd = ( ( b->w ) + ( a->w ) ) / 2.0f;
float YAdd = ( ( b->h ) + ( a->h ) ) / 2.0f;
// Check if the Distance vector is below 0.0f
absDistance.x = ( Distance.x < 0.0f ) ? -Distance.x : Distance.x;
absDistance.y = ( Distance.y < 0.0f ) ? -Distance.y : Distance.y;
/*If the absDistance X is less than X add and the absDistance is less thank YAdd
then it dosen't take a genius to figure out they arn't colliding so return false*/
if( ! ( ( absDistance.x < XAdd ) && ( absDistance.y < YAdd ) ) ) {
return false;
}
/*Get the magnitute by the overlap of the two rectangles*/
XMagnitute = XAdd - absDistance.x;
YMagnitute = YAdd - absDistance.y;
/*Determin what axis we need to act on based on the overlap*/
if( XMagnitute < YMagnitute ) {
normal.x = ( Distance.x > 0) ? -XMagnitute : XMagnitute;
}
else if ( XMagnitute > YMagnitute ) {
normal.y = ( Distance.y > 0) ? -YMagnitute : YMagnitute;
}
// If we reached this point then we now know the was a collision
return true;
}
Code: Select all
normal.x = 0;
normal.y = 0;
Code: Select all
Vector2 Distance;
Vector2 absDistance;
float XMagnitute;
float YMagnitute;
Code: Select all
Distance.x = ( ( b->x ) - ( a->x ) );
Distance.y = ( ( b->y ) - ( a->y ) );
Code: Select all
float XAdd = ( ( b->w ) + ( a->w ) ) / 2.0f;
float YAdd = ( ( b->h ) + ( a->h ) ) / 2.0f;
Code: Select all
absDistance.x = ( Distance.x < 0.0f ) ? -Distance.x : Distance.x;
absDistance.y = ( Distance.y < 0.0f ) ? -Distance.y : Distance.y;
Code: Select all
if( ! ( ( absDistance.x < XAdd ) && ( absDistance.y < YAdd ) ) ) {
return false;
}
No point going any further, return false and be done with it.
Code: Select all
XMagnitute = XAdd - absDistance.x;
YMagnitute = YAdd - absDistance.y;
Code: Select all
if( XMagnitute < YMagnitute ) {
normal.x = ( Distance.x > 0) ? -XMagnitute : XMagnitute;
}
else if ( XMagnitute > YMagnitute ) {
normal.y = ( Distance.y > 0) ? -YMagnitute : YMagnitute;
}
Now we can say something like
Code: Select all
Vector2 norm;
if ( IsCollision(&a,&b,norm) ) {
a.x += norm.x;
a.y += norm.y;
}
Collision:
None Collision:
Notice how the red flashing has gone with the collision?
That's because it's now pushing the object away so it can't intersect with it.
Circular Collision Detection (Because nobody ever talks about it)
This is something I've never seen discussed. But it's pretty straightforward and efficient.
The main advantage to collision detection with a circle is that it has a set radius equal all around it.
Code: Select all
bool IsCollision(Circle a, Circle b) {
float distX = b.x - a.x;
float distY = b.y - a.y;
int radist = a.radius + b.radius;
if ( distX * distX + distY * distY < radist * radist) {
return true;
}
return false;
}
I hope this helps someone, a while back it would've helped me.
I've tested all the code as seen but let me know if you find anything wrong with it.