Circular Collision Detection
Last Updated 4/09/14
Besides rectangles, the most common shape you'll have to deal with is a circle. In the last tutorial we had to use 11 collision boxes for a circle. This tutorial will teach you a more efficient way to handle circles and collision detection.A Circular Collision Detection tutorial with SDL 2 is now available.
#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
#include <string>
#include <vector>
#include <cmath>
This tutorial is going to require the distance formula so we include a header for math.
//A circle stucture
struct Circle
{
int x, y;
int r;
};
We have to create our own circle structure for this program. "x" and "y" are the offsets of the center of the
circle. "r" is the radius.
//The dot
class Dot
{
private:
//The area of the dot
Circle c;
//The velocity of the dot
int xVel, yVel;
public:
//Initializes the variables
Dot();
//Takes key presses and adjusts the dot's velocity
void handle_input();
//Moves the dot
void move( std::vector<SDL_Rect> &rects, Circle &circle );
//Shows the dot on the screen
void show();
};
Here's yet another revision of the Dot class.
Everything is pretty much the same except for two differences. This time we have a Circle structure instead of a vector of SDL_Rects. Also, in the move() function we check collision with a vector of SDL_Rects and a Circle.
Everything is pretty much the same except for two differences. This time we have a Circle structure instead of a vector of SDL_Rects. Also, in the move() function we check collision with a vector of SDL_Rects and a Circle.
double distance( int x1, int y1, int x2, int y2 )
{
//Return the distance between the two points
return sqrt( pow( x2 - x1, 2 ) + pow( y2 - y1, 2 ) );
}
This function we made gives us the distance between given 2 points. This is pretty much the only real math used in
this program.
For those of you using visual studio you may need to type cast those integers to doubles.
For those of you using visual studio you may need to type cast those integers to doubles.
bool check_collision( Circle &A, Circle &B )
{
//If the distance between the centers of the circles is less than the sum of their radii
if( distance( A.x, A.y, B.x, B.y ) < ( A.r + B.r ) )
{
//The circles have collided
return true;
}
//If not
return false;
}
Checking collision between two circles is pretty easy. All you have to do is check whether or not the distance
between the centers of the circles is less than the sum of their radii.
If it is less, a collision has happened, otherwise there's no collision.
If it is less, a collision has happened, otherwise there's no collision.
bool check_collision( Circle &A, std::vector<SDL_Rect> &B )
{
//Closest point on collision box
int cX, cY;
//Go through the B boxes
for( int Bbox = 0; Bbox < B.size(); Bbox++ )
{
This function checks collision between a circle and a vector of rectangles. Checking for collision between a circle
and a rectangle gets a bit tricky. To check if there's a collision between a circle and a collision box, you must
find the point on the collision box closest to the center of the circle.
//Find closest x offset
if( A.x < B[ Bbox ].x )
{
cX = B[ Bbox ].x;
}
If the center of the circle is to the left of the box, then the x offset of the closest point is equal to the x
offset of the collision box.
else if( A.x > B[ Bbox ].x + B[ Bbox ].w )
{
cX = B[ Bbox ].x + B[ Bbox ].w;
}
If the center of the circle is to the right of the box, then the x offset of the closest point is equal to the x
offset of the right side of the collision box.
else
{
cX = A.x;
}
If the x offset of the center of the circle is not to the left or the right of the collision box, then the x
offset is inside the collision box.
//Find closest y offset
if( A.y < B[ Bbox ].y )
{
cY = B[ Bbox ].y;
}
else if( A.y > B[ Bbox ].y + B[ Bbox ].h )
{
cY = B[ Bbox ].y + B[ Bbox ].h;
}
else
{
cY = A.y;
}
Then we do the same as above to find the closest y offset.
//If the closest point is inside the circle
if( distance( A.x, A.y, cX, cY ) < A.r )
{
//This box and the circle have collided
return true;
}
}
//If the shapes have not collided
return false;
}
If the point on the collision box closest to the circle is inside the circle, then the circle and the collision
box are overlapped. Here we keep going through all the collision boxes until we find a collision or all of the
boxes have been checked and there is no collision.
//Make the shapes
std::vector<SDL_Rect> box( 1 );
Circle otherDot;
//Set the shapes' attributes
box[ 0 ].x = 60;
box[ 0 ].y = 60;
box[ 0 ].w = 40;
box[ 0 ].h = 40;
otherDot.x = 30;
otherDot.y = 30;
otherDot.r = DOT_WIDTH / 2;
In the main() function we create the 2 shapes the Dot is going to interact with.
//While the user hasn't quit
while( quit == false )
{
//Start the frame timer
fps.start();
//While there's events to handle
while( SDL_PollEvent( &event ) )
{
//Handle events for the dot
myDot.handle_input();
//If the user has Xed out the window
if( event.type == SDL_QUIT )
{
//Quit the program
quit = true;
}
}
//Move the dot
myDot.move( box, otherDot );
//Fill the screen white
SDL_FillRect( screen, &screen->clip_rect, SDL_MapRGB( screen->format, 0xFF, 0xFF, 0xFF ) );
//Show the box
SDL_FillRect( screen, &box[ 0 ], SDL_MapRGB( screen->format, 0x00, 0x00, 0x00 ) );
//Show the other dot
apply_surface( otherDot.x - otherDot.r, otherDot.y - otherDot.r, dot, screen );
//Show our dot
myDot.show();
//Update the screen
if( SDL_Flip( screen ) == -1 )
{
return 1;
}
//Cap the frame rate
if( fps.get_ticks() < 1000 / FRAMES_PER_SECOND )
{
SDL_Delay( ( 1000 / FRAMES_PER_SECOND ) - fps.get_ticks() );
}
}
Here's the main loop, pretty much everything is the same story as before.
I would like to point one thing out.
When we show the other dot, we don't blit the image at its offset. The offset is the center of the circle, so we have to blit the dot image at the upper-left corner. We do this by subtracting the radius from the offset.
When we show the other dot, we don't blit the image at its offset. The offset is the center of the circle, so we have to blit the dot image at the upper-left corner. We do this by subtracting the radius from the offset.