SDL_Rect collision detection

Started by
8 comments, last by JoeMagoe 14 years, 9 months ago
I'm new to SDL and I'm trying to create a brick break game where the user breaks blocks with a ball. The user can hit the ball with with paddle that moves at the bottom of the screen. Right now it seems that the collision detection is not working with the paddle or the first block that I added in. Basically, the collision box appears where the paddle is, but does not move with the paddle. It just stays invisible where the paddle first appears on the screen and the ball still bounces off of the invisible paddle rect, but not the paddle itself Then the collision box for the block is appearing below the block and in the block. I've been working at this for days and I'm not sure why my collision isn't working. Any help would be greatly appreciated. //The headers #include "SDL/SDL.h" #include "SDL/SDL_image.h" #include <string> #include <vector> #include <cmath> //The screen attributes const int SCREEN_WIDTH = 800; const int SCREEN_HEIGHT = 600; const int SCREEN_BPP = 32; //The frame rate const int FRAMES_PER_SECOND = 20; ////The dimensions of the dot //const int DOT_WIDTH = 20; //const int DOT_HEIGHT = 20; // ////The dimensions of the paddle //const int PADDLE_WIDTH = 112; //const int PADDLE_HEIGHT = 20; //The surfaces SDL_Surface *dot = NULL; SDL_Surface *paddle = NULL; SDL_Surface *screen = NULL; SDL_Surface *background = NULL; SDL_Surface *block1_image = NULL; //The event structure SDL_Event event; //Create objects SDL_Rect ball; SDL_Rect box; SDL_Rect block1; //The dot that will move around on the screen class Dot { private: //The area of the dot SDL_Rect ball; //The velocity of the dot int xVel, yVel; public: //Initializes the variables Dot(); //Moves the dot void move(); //Shows the dot on the screen void show(); }; //The paddle that will move around on the screen class Paddle { private: //The collision box of the square SDL_Rect box; //The velocity of the paddle int xVel, yVel; public: //Initializes the variables Paddle(); //Takes key presses and adjusts the paddle's velocity void handle_input(); //Moves the paddle void move1(); //Shows the paddle on the screen void show1(); }; class Block { private: //collision box for blocks SDL_Rect block1; public: Block(); void show2(); }; //The timer class Timer { private: //The clock time when the timer started int startTicks; //The ticks stored when the timer was paused int pausedTicks; //The timer status bool paused; bool started; public: //Initializes variables Timer(); //The various clock actions void start(); void stop(); void pause(); void unpause(); //Gets the timer's time int get_ticks(); //Checks the status of the timer bool is_started(); bool is_paused(); }; SDL_Surface *load_image( std::string filename ) { //The image that's loaded SDL_Surface* loadedImage = NULL; //The optimized surface that will be used SDL_Surface* optimizedImage = NULL; //Load the image loadedImage = IMG_Load( filename.c_str() ); //If the image loaded if( loadedImage != NULL ) { //Create an optimized surface optimizedImage = SDL_DisplayFormat( loadedImage ); //Free the old surface SDL_FreeSurface( loadedImage ); //If the surface was optimized if( optimizedImage != NULL ) { //Color key surface SDL_SetColorKey( optimizedImage, SDL_SRCCOLORKEY, SDL_MapRGB( optimizedImage->format, 0, 0xFF, 0xFF ) ); } } //Return the optimized surface return optimizedImage; } void apply_surface( int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip = NULL ) { //Holds offsets SDL_Rect offset; //Get offsets offset.x = x; offset.y = y; //Blit SDL_BlitSurface( source, clip, destination, &offset ); } bool check_collision( SDL_Rect A, SDL_Rect B ) { //The sides of the rectangles int leftA, leftB; int rightA, rightB; int topA, topB; int bottomA, bottomB; //Calculate the sides of rect A leftA = A.x + 1; rightA = A.x + A.w - 1; topA = A.x + 1; bottomA = A.y + A.h - 1; //Calculate the sides of rect B leftB = B.x + 1; rightB = B.x + B.w - 1; topB = B.y + 1; bottomB = B.y + B.h - 1; //If any of the sides from A are outside of B if( bottomA <= topB ) { return false; } if( topA >= bottomB ) { return false; } if( rightA <= leftB ) { return false; } if( leftA >= rightB ) { return false; } //If none of the sides from A are outside B return true; } bool init() { //Initialize all SDL subsystems if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 ) { return false; } //Set up the screen screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE ); //If there was an error in setting up the screen if( screen == NULL ) { return false; } //Set the window caption SDL_WM_SetCaption( "Game Project", NULL ); //If everything initialized fine return true; } bool load_files() { //Load the background image background = load_image( "background.png" ); //Load the dot image dot = load_image( "dot.png" ); //Load the dot image paddle = load_image( "paddle.png" ); block1_image = load_image("block.bmp"); //If there was a problem in loading the dot if( dot == NULL ) { return false; } //If there was a problem in loading the dot if( paddle == NULL ) { return false; } if(block1_image == NULL) { return false; } //If there was a problem in loading the background if( background == NULL ) { return false; } //If everything loaded fine return true; } void clean_up() { //Free the surface SDL_FreeSurface( dot ); SDL_FreeSurface( paddle ); SDL_FreeSurface( block1_image); SDL_FreeSurface( background ); //Quit SDL SDL_Quit(); } Dot::Dot() { //Initialize the offsets ball.x = 300; ball.y = 400; //Set the paddle's dimentions ball.w = 20; ball.h = 20; //Initialize the velocity xVel = 15; yVel = 15; } Paddle::Paddle() { //Initialize the offsets box.x = 300; box.y = 500; //Set the square's dimentions box.w = 115; box.h = 20; //Initialize the velocity xVel = 0; yVel = 0; } Block::Block() { //Initialize the offsets block1.x = 100; block1.y = 200; block1.w = 90; block1.h = 26; } void Paddle::handle_input() { //If a key was pressed if( event.type == SDL_KEYDOWN ) { //Adjust the velocity switch( event.key.keysym.sym ) { case SDLK_LEFT: xVel -= box.w / 6 ; break; case SDLK_RIGHT: xVel += box.w / 6; break; } } //If a key was released else if( event.type == SDL_KEYUP ) { //Adjust the velocity switch( event.key.keysym.sym ) { case SDLK_LEFT: xVel += box.w / 6; break; case SDLK_RIGHT: xVel -= box.w / 6; break; } } } void Dot::show() { //Show the dot apply_surface( ball.x, ball.y, dot, screen ); } void Dot::move() { //Move the dot left or right ball.x += xVel; //If the dot went too far to the left or right or has collided with the other shapes if( ( ball.x > SCREEN_WIDTH - ball.w ) || ( ball.x < 0 ) || ( check_collision( ball, box ) ) ) { //Move back ball.x -= xVel; xVel *= -1; } //Move the dot up or down ball.y += yVel; if( ( ball.y > SCREEN_HEIGHT - ball.h ) || ( ball.y < 0 ) || ( check_collision( ball, box ) ) || (check_collision(ball, block1)) ) { //Move back ball.y -= yVel; yVel *= -1; } } void Paddle::show1() { //Show the paddle apply_surface( box.x, box.y, paddle, screen ); } void Paddle::move1() { //Move the square left or right box.x += xVel; //If the square went too far to the left or right or has collided with the wall if( ( box.x > SCREEN_WIDTH - box.w ) || ( check_collision( box, ball ) ) ) { //Move back box.x -= xVel; } else if( box.x < 0) box.x += xVel; } void Block::show2() { //Show the paddle apply_surface( block1.x, block1.y, block1_image, screen ); } Timer::Timer() { //Initialize the variables startTicks = 0; pausedTicks = 0; paused = false; started = false; } void Timer::start() { //Start the timer started = true; //Unpause the timer paused = false; //Get the current clock time startTicks = SDL_GetTicks(); } void Timer::stop() { //Stop the timer started = false; //Unpause the timer paused = false; } void Timer::pause() { //If the timer is running and isn't already paused if( ( started == true ) && ( paused == false ) ) { //Pause the timer paused = true; //Calculate the paused ticks pausedTicks = SDL_GetTicks() - startTicks; } } void Timer::unpause() { //If the timer is paused if( paused == true ) { //Unpause the timer paused = false; //Reset the starting ticks startTicks = SDL_GetTicks() - pausedTicks; //Reset the paused ticks pausedTicks = 0; } } int Timer::get_ticks() { //If the timer is running if( started == true ) { //If the timer is paused if( paused == true ) { //Return the number of ticks when the timer was paused return pausedTicks; } else { //Return the current time minus the start time return SDL_GetTicks() - startTicks; } } //If the timer isn't running return 0; } bool Timer::is_started() { return started; } bool Timer::is_paused() { return paused; } int main( int argc, char* args[] ) { //Quit flag bool quit = false; //The dot that will be used Dot myDot; box.x = 300; box.y = 500; box.h = 20; box.w = 115; //The paddle that will be used Paddle myPaddle; //The block that will be used Block myBlock; block1.x = 100; block1.y = 200; block1.w = 90; block1.h = 26; //The frame rate regulator Timer fps; //Initialize if( init() == false ) { return 1; } //Load the files if( load_files() == false ) { return 1; } //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 myPaddle.handle_input(); //If the user has Xed out the window if( event.type == SDL_QUIT ) { //Quit the program quit = true; } } //Apply the background apply_surface( 0, 0, background, screen ); //Move the paddle myDot.move(); //Move the paddle myPaddle.move1(); //Fill the screen white SDL_FillRect( screen, &screen->clip_rect, SDL_MapRGB( screen->format, 0xFF, 0xFF, 0xFF ) ); //Show the dot on the screen myDot.show(); //Show the dot on the screen myPaddle.show1(); //Show the blocks on the screen myBlock.show2(); //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() ); } } //Clean up clean_up(); return 0; }
Advertisement
Is it possible for you to give a description of what is actually happening? As in, is it not compiling or is it just not detecting collision at all? And please find the problem area and post it, as I personally would not like to wade through a large block of code.
It's compiling and running, but the collision detection is not working on the paddle or the blocks (bricks). The collision box is where the paddle starts off, but when the user moves the paddle, the collision box does not move with it. It stays where the paddle starts off and is an invisible wall where the ball bounces off of it. As for the bricks, or blocks as they are called in the code, has a collision box, but another invisible one appears below it. I'm not sure where the problem is occuring in the code or why it is happening. I tried to give the code that I thought might be causing a problem in this post, but if you need more of a reference, the complete code is in the original post.

I think the problematic portions of the code would be this:

void apply_surface( int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip = NULL )
{
//Holds offsets
SDL_Rect offset;

//Get offsets
offset.x = x;
offset.y = y;

//Blit
SDL_BlitSurface( source, clip, destination, &offset );
}



/////////////////////////////////////////////////////////

bool check_collision( SDL_Rect A, SDL_Rect B )
{
//The sides of the rectangles
int leftA, leftB;
int rightA, rightB;
int topA, topB;
int bottomA, bottomB;

//Calculate the sides of rect A
leftA = A.x + 1;
rightA = A.x + A.w - 1;
topA = A.x + 1;
bottomA = A.y + A.h - 1;

//Calculate the sides of rect B
leftB = B.x + 1;
rightB = B.x + B.w - 1;
topB = B.y + 1;
bottomB = B.y + B.h - 1;

//If any of the sides from A are outside of B
if( bottomA <= topB )
{
return false;
}

if( topA >= bottomB )
{
return false;
}

if( rightA <= leftB )
{
return false;
}

if( leftA >= rightB )
{
return false;
}

//If none of the sides from A are outside B
return true;
}

//////////////////////////////////////////////

Paddle::Paddle()
{
//Initialize the offsets
box.x = 300;
box.y = 500;

//Set the square's dimentions
box.w = 115;
box.h = 20;

//Initialize the velocity
xVel = 0;
yVel = 0;
}

Block::Block()
{
//Initialize the offsets
block1.x = 100;
block1.y = 200;
block1.w = 90;
block1.h = 26;
}


void Paddle::handle_input()
{
//If a key was pressed
if( event.type == SDL_KEYDOWN )
{
//Adjust the velocity
switch( event.key.keysym.sym )
{

case SDLK_LEFT: xVel -= box.w / 6 ; break;
case SDLK_RIGHT: xVel += box.w / 6; break;
}
}
//If a key was released
else if( event.type == SDL_KEYUP )
{
//Adjust the velocity
switch( event.key.keysym.sym )
{

case SDLK_LEFT: xVel += box.w / 6; break;
case SDLK_RIGHT: xVel -= box.w / 6; break;
}
}
}
////////////////////////////////////////////////

void Paddle::show1()
{
//Show the paddle
apply_surface( box.x, box.y, paddle, screen );
}

void Paddle::move1()
{
//Move the square left or right
box.x += xVel;

//If the square went too far to the left or right or has collided with the wall
if( ( box.x > SCREEN_WIDTH - box.w ) || ( check_collision( box, ball ) ) )
{
//Move back
box.x -= xVel;
}
else if( box.x < 0)
box.x += xVel;

}

Surely you thought that posting that much code oustide of source tags would make it unreadable, right? There is a link to the faq at the top right corner of the screen. It will tell you which source tags to use.

Hmmm... Your collision boxes are set correctly (I fill each one with red, and each matches up with their corresponding object). So, this means that your collision detection is doing something funky. And, by the way, you should make one single class for all three of these things and, also, you don't need to name functions differently if they're in different classes (for example, you don't need a show1() in Paddle and a show2() in Block). I'll look at your code further to see what the problem is.
Hey, wait, you have two SDL_Rect's for each collision box declared in your program, once globally, and once in the class. Why so?
Hmmm... Honestly, this is not very well-structured code. I don't understand why you did some of the things you did. This is not an SDL problem as much of a basic C++ one. Though I have no hard feelings, I'd request you to read a C++ book and then come back and fix this code. Sorry, but I don't think I can fix this unless I rewrite most of it, and I'm not willing to do that. I'll attempt to next morning, but I'd suggest you try to do so now. Again, I don't mean to be rude, but I really can't help you, for I'd be doing everything. Good luck!
It's messy because I'm working with another programmer on it and we have been updating each other's version back and forth. I did not create the class structure or the functions for it, he did and I'm trying to help him with the paddle and its collision detection. Code clean up will come after we get the initial parts of the game working.

As for the rects globally and in the class, we had to do this because it would not work in collision detection. For instance, when the collision detection was called for (ball, paddle) inside the function move() there was an error stating that paddle was not in the scope of that funciton.
From what i can see it seems you're using the wrong collision box when determining if the ball is colliding with the paddle or not in the ball's move() function:
void Dot::move()if( ( ball.x &gt; SCREEN_WIDTH - ball.w ) || ( ball.x &lt; 0 ) || ( check_collision( ball, box ) ) )if( ( ball.y &gt; SCREEN_HEIGHT - ball.h ) || ( ball.y &lt; 0 ) || ( check_collision( ball, box ) ) || (check_collision(ball, block1)) )

You are here checking if you are colliding with the SDL_Rect box you made globaly in the start of the program and is set in the main() function:
box.x = 300;box.y = 500;box.h = 20;box.w = 115;

This box is never updated, therefor it doesn't move with the paddel.

what you need to do is change the box you're checking collision with at the moment with the box form the paddle class
Quote:Original post by Mowkage
It's messy because I'm working with another programmer on it and we have been updating each other's version back and forth. I did not create the class structure or the functions for it, he did and I'm trying to help him with the paddle and its collision detection. Code clean up will come after we get the initial parts of the game working.

As for the rects globally and in the class, we had to do this because it would not work in collision detection. For instance, when the collision detection was called for (ball, paddle) inside the function move() there was an error stating that paddle was not in the scope of that funciton.


Oh, OK, this makes more sense. I'm sorry for assuming you had created this by yourself (I made an @$$ out of you and me). Still, I suggest keeping your code clean from the start so that any problems you face are because of some minor mistake somewhere or a decrepit function or something. The mistakes are easier to fix when there isn't other stuff getting in the way. For example, this time, your double-declaration messed up your collision detection. It's good you have a solution now, though.

This topic is closed to new replies.

Advertisement