Collision Detection
An SDL padawan you are.
Drawn images to the screen using SDL_BlitSurface you have.
How to detect button presses you know not!
mMMmm, much to learn you still have!
Collision detection isn’t just for video games! Imagine, your finger colliding with an image and then something epic happening. This post is going to explore a simple way to detect when things collide.
What I will cover in this post is a very simple form of collision detection. Quite simply, we will look at an X,Y coordinate for a tap event and then figure out if it fits inside a specified rectangle. Before we begin, I should mention something that is very important about SDL_Rect. The diligent student may notice that I never set the width/height of a rectangle in the below code. Well, it just happens that when you call SDL_BlitSurface and specify a destination rectangle, the destination rectangle is modified to contain the width/height information of the bitmap you are blitting. Keep this in mind.
Is this lazy coding? Should we manually set rectangle dimensions so that we don’t have to worry about whether they’ve been blit’d? Please comment if you have an opinion. Now to the code.
#include SDL.h #include PDL.h #include SDL_image.h //Surfaces SDL_Surface* screen; SDL_Surface* moon; SDL_Surface* ship; SDL_Surface* explosion; //Rectangles SDL_Rect moonLocation; SDL_Rect shipLocation; //Misc PDL_ScreenMetrics screenMetrics; SDL_Event sdlEvent; bool isPointInRect(SDL_Rect r, Uint16 x, Uint16 y){ return (r.x = x) (x = r.x + r.w) (r.y = y) (y = r.y + r.h); } int main(int argc, char** argv) { //Init SDL_Init(SDL_INIT_VIDEO); PDL_Init(0); //Set screen resolution and check for errors PDL_Err err = PDL_GetScreenMetrics(screenMetrics); if(err == PDL_INVALIDINPUT) return -1; if(err == PDL_EOTHER) screen = SDL_SetVideoMode(320, 480, 0, SDL_SWSURFACE); else screen = SDL_SetVideoMode(screenMetrics.horizontalPixels, screenMetrics.verticalPixels, 0, SDL_SWSURFACE); //Load the images moon = IMG_Load(moon.png); ship = IMG_Load(ship.png); explosion = IMG_Load(explosion.png); if(moon == NULL || ship == NULL || explosion == NULL) return -2; moonLocation.x = screen-w/2 - moon-w/2; moonLocation.y = screen-h/2 - moon-h/2; //Draw the moon to the screen SDL_BlitSurface(moon, NULL, screen, moonLocation); SDL_Flip(screen); //The Event processing loop do { //Loop through all of the current events while(SDL_PollEvent(sdlEvent)){ //Tap event if(sdlEvent.type == SDL_MOUSEBUTTONDOWN){ //Move the ship to the clicked location shipLocation.x = sdlEvent.button.x - ship-w/2; shipLocation.y = sdlEvent.button.y - ship-h/2; //Clear the entire screen SDL_FillRect(screen, NULL, SDL_MapRGB(screen-format, 0, 0, 0)); //Draw the moon SDL_BlitSurface(moon, NULL, screen, moonLocation); //If the location is inside the moon, draw an explosion if(isPointInRect(moonLocation, sdlEvent.button.x, sdlEvent.button.y)) SDL_BlitSurface(explosion, NULL, screen, shipLocation); else //Draw the ship SDL_BlitSurface(ship, NULL, screen, shipLocation); //Draw the scene SDL_Flip(screen); } //Keyboard Event else if(sdlEvent.type == SDL_KEYDOWN){ //Read the actual key that is down switch(sdlEvent.key.keysym.sym) { //Handle the escape key by exiting the program case SDLK_ESCAPE: sdlEvent.type = SDL_QUIT; break; default: break; } } } //Don't be a cpu hog SDL_Delay(1); } while (sdlEvent.type != SDL_QUIT); PDL_Quit(); SDL_Quit(); return 0; }
The majority of this code has been covered in previous WebOS PDK articles.
Collision Function
Let us begin with lines 19-24. This function returns true if a point is contained inside a rectangle. The function takes 3 parameters, an SDL_Rect along with an x,y coordinate.
bool isPointInRect(SDL_Rect r, Uint16 x, Uint16 y){ return (r.x = x) (x = r.x + r.w) (r.y = y) (y = r.y + r.h); }
The condition for a point to be inside a box is quite easy to describe and can be done using 2 inequalities. As an aid, it may help to see a visual representation of this.
Ok, just look at the rectangle for a moment. Notice what the minimums and maximums for X and Y are:
X Min: x
X Max: x+w
Y Min: y
Y Max: y+h
Given a point P that has component values for x and y, we can determine if the point is in the rectangle by comparing it to the min and max values of x and y. First, the x component:
x <= P.x <= x+w
Then the y component:
y <= P.y <= y+h
If both statements are true then we are inside the rectangle. The code in lines 20-23 just breaks up the above 2 inequalities into 4 inequalities, left side first, then right side. It’s quite simple once you can visualize the min/max of the rectangle.
Blowing Things Up
62-63 set the location of the ship based on where the user taps. The ship is centered on the tap. Note that we are using shipLocation for both the location of the ship and the explosion.
shipLocation.x = sdlEvent.button.x - ship-w/2; shipLocation.y = sdlEvent.button.y - ship-h/2;
What we do next is clear the screen (66). Note that this call to SDL_FillRect can be troublesome if you attempt to take shortcuts like I did. You see, I figured that I would save some time by throwing in 0000000 as the color value. This worked fine in Visual Studio but resulted in a permanent black screen on the device. You can read through my misadventures if you are curious. The important lesson is to use SDL_MapRGB to get the resulting Uint32 for the color.
SDL_FillRect(screen, NULL, SDL_MapRGB(screen-format, 0, 0, 0));
72-75 is where we call the collision detection function and draw either an explosion or a ship, based on the results.
//If the location is inside the moon, draw an explosion if(isPointInRect(moonLocation, sdlEvent.button.x, sdlEvent.button.y)) SDL_BlitSurface(explosion, NULL, screen, shipLocation); else //Draw the ship SDL_BlitSurface(ship, NULL, screen, shipLocation);
That’s it, here is a short video proving that it works!
Credits:
Explosion png was found at Bothel University of Washington.
Moon png is from Nasa gallery.
Ship png created by myself.
Be the first to comment