SDL_Rect collision detection
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;
}
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;
}
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.
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:
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:
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
void Dot::move()if( ( ball.x > SCREEN_WIDTH - ball.w ) || ( ball.x < 0 ) || ( check_collision( ball, box ) ) )if( ( ball.y > SCREEN_HEIGHT - ball.h ) || ( ball.y < 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
Popular Topics
Advertisement