Breakout Clone - SDL

I'm developing a basic Breakout clone with SDL and I'm running into a few difficult pieces along the way. I have most of the game completed with the exception of the score text and the collision structure. The score text is the easy bit, but the collision detection/response is where I'm getting really stuck.

I've been using lazyfoo's tutorials, and as annoying as I find his code to be.. it's helped a lot. The issue I'm having is that I don't want to just detect a collision and have the ball stop at the bricks; I want the ball to deflect off of the bricks and the paddle at a specific angle. Do you have any helpful hints/tips as to how I should go about this?

Classes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
class Timer
{
     int startTicks;
     int pausedTicks;
     bool paused;
     bool started;
    public:
     Timer() {
      startTicks = 0;
      pausedTicks = 0;
      paused = false;
      started = false; }
     void start() {
       started = true; 
       paused = false;
       startTicks = SDL_GetTicks(); }
     void stop() {
      started = false; paused = false; }
     void pause() {
      if((started==true)&&(paused==true)) {
        paused = true;
        pausedTicks = SDL_GetTicks()-startTicks; }
     }
     void unpause() {
      if(paused==true) {
        paused = false; 
        startTicks = SDL_GetTicks()-pausedTicks;
        pausedTicks = 0; }
     }
     int get_ticks() { 
       if(started==true) {
          if(paused==true)
          { return pausedTicks; }
          else
          { return SDL_GetTicks() - startTicks; }    
       }
     return 0;
     }
     bool isStarted() { return started; }
     bool isPaused() { return paused; }
};

class Ball
{
  int x, y, r; 
  int xVel; int yVel; 
 public:     
   Ball(){ int x = 200; y = 200; r = 10; xVel = 0; yVel = 0; }
   void changeVel(int xV, int yV){ xVel=xV; yV=yVel; }
   void move()
   { x+=xVel; y+=yVel; }
   void show()
   { applySurface(x,y,ball,screen); }
};
class Score
{
  int ballCount;
  int score;
 public:
   Score(){ ballCount = 3; score = 0; }
   int getBalls(){ return ballCount; }
   int getScore(){ return score; }
   void addScore(int points){ score+=points; }
   void reduceBalls(){ ballCount-=1; }
};
class Pad 
{
  int x, y;
  int xVel, yVel;
  int padWidth, padHeight; 
  SDL_Rect padBox; 
 public:
   Pad() 
   { 
     x=200; y=450; xVel=0; yVel=0; 
     padWidth = 80; padHeight = 30; 
     //Will need to update x and y for collision
     // detection in the move() function I believe. 
     padBox.w = 64; padBox.h = 30;
     padBox.x = 200; padBox.y = 450; 
   }
   void handleInput() 
   {
     if(event.type==SDL_KEYDOWN)
     {
       switch(event.key.keysym.sym) 
       {
         case SDLK_a: xVel-= 10; break;
         case SDLK_d: xVel+= 10; break; 
       }
     }
     else if(event.type==SDL_KEYUP) 
     {
       switch(event.key.keysym.sym) 
       {
         case SDLK_a: xVel+= 10; break;
         case SDLK_d: xVel-= 10; break;
       }
     }
   }
   void move()
   {
     x+= xVel;
     if((x<0)||(x+padWidth>scrWidth))
     { x-=xVel; padBox.x = x; }
   }
   void show()
     { applySurface(x,y,platform,screen); }
};
class Brick
{
 int width, height;
 int x, y; 
 int type;
 SDL_Rect box;
 bool visible; 
 public:
     Brick(int xPos, int yPos, int brickType) 
     { width = 64; height = 30; 
       x = xPos; y = yPos; 
       type = brickType; visible = true; 
       box.x = x; box.y = y;
       box.w = width; box.h = height; }
   void show() { 
    if(visible) {
       switch(type) {
        case 1: 
          applySurface(x,y,brick1,screen); break;
        case 2:
          applySurface(x,y,brick2,screen); break;
        case 3:
          applySurface(x,y,brick3,screen); break;
        case 4:
          applySurface(x,y,brick4,screen); break;
        }
    }
   }
   void hide() { visible = false; }
};
vector<Brick> brickLayout;
vector<Brick>::iterator it;

Everything Else
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
#include <string>
#include <vector>
#include <cmath> 
using namespace std; 

struct Circle { int x, y, r; };
bool init();
bool loadFiles();
void cleanUp(); 
SDL_Surface *loadImage(string filename);
void applySurface(int x,int y,SDL_Surface *source,SDL_Surface *destination,
                  SDL_Rect *clip=NULL);
bool checkCollision(Circle &A, vector<SDL_Rect> &B);
void setupBricks();
void showBricks(); 
double distance(int x1, int y1, int x2, int y2);

const int scrWidth = 640;
const int scrHeight = 480;
const int scrBPP = 32;
const int FPS = 60; 
const int brickWidth = 64;
const int brickHeight = 30; 

string title = "Break Out";
SDL_Surface *background = NULL; 
SDL_Surface *screen = NULL;
SDL_Surface *ball = NULL; 
SDL_Surface *platform = NULL;
SDL_Surface *brick1 = NULL;
SDL_Surface *brick2 = NULL;
SDL_Surface *brick3 = NULL;
SDL_Surface *brick4 = NULL; 
SDL_Event event;

int main(int argc, char **argv)
{
 Timer fps; 
 Pad myPad; 
 Score myScore;
 Ball myBall; 
 int brickX = 0; int brickY = 0; 
 bool quit = false;
 if(init()==false){return 1;}
 if(loadFiles()==false){return 1;}
 setupBricks();
 while(quit==false) 
 {
   while(myScore.getBalls()>0) {
    fps.start(); 
    while(SDL_PollEvent(&event))
    { 
      myPad.handleInput();
      if(event.type==SDL_QUIT){quit=true;} 
    }
   applySurface(0,0,background,screen);
   showBricks(); 
   myBall.move();
   myPad.move();
   myBall.show();
   myPad.show();
   if(SDL_Flip(screen)==-1){return 1;}
   if(fps.get_ticks()<1000/FPS)
   { SDL_Delay( (1000/FPS)-fps.get_ticks()); }
  }
 }
 cleanUp();   
 return 0;    
}
void setupBricks()
{
  int brickX = 0; int brickY = 0; 
  for(int row=1;row<=4;row++)
  {
    for(int i=1;i<=10;i++)
    { 
      brickLayout.push_back(Brick(brickX,brickY,row)); 
      brickX += brickWidth;
    }
    brickX = 0;
    brickY += brickHeight;  
  }     
}
void showBricks()
{
  for(it = brickLayout.begin(); it<=brickLayout.end(); it++)
  { it->show(); }
}
double distance(int x1, int y1, int x2, int y2)
{
 double d_x1 = x1; double d_x2 = x2;
 double d_y1 = y1; double d_y2 = y2;
 return sqrt( pow((d_x2-d_x1),2) + pow((d_y2-d_y1),2) );
}
bool checkCollision(Circle &A, vector<SDL_Rect> &B)
{
 int cX, cY;
 for(int Bbox = 0; Bbox < B.size(); Bbox++)
 {
   if(A.x < B[Bbox].x)
   { cX = B[Bbox].x; }
   else if(A.x > B[Bbox].x + B[Bbox].w)
   { cX = B[Bbox].x + B[Bbox].w; }
   else { cX = A.x; }
   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; }
   if(distance(A.x,A.y,cX,cY) < A.r)
   { return true; }
 }
 return false;
}
SDL_Surface *loadImage(string filename)
{
  SDL_Surface *loadedImage = NULL;
  SDL_Surface *optimizedImage = NULL;
  loadedImage = IMG_Load (filename.c_str());
  if(loadedImage!=NULL) {
    optimizedImage = SDL_DisplayFormat(loadedImage);
    SDL_FreeSurface(loadedImage);
  }
  if(optimizedImage != NULL) {
    Uint32 colorKey = SDL_MapRGB(optimizedImage->format,0,0xFF,0xFF);
    SDL_SetColorKey(optimizedImage,SDL_SRCCOLORKEY,colorKey);
   return optimizedImage; 
  }
}
void applySurface(int x,int y,SDL_Surface *source,SDL_Surface *destination,
                  SDL_Rect *clip)
{
 SDL_Rect offset;
 offset.x = x;
 offset.y = y;
 SDL_BlitSurface(source,clip,destination,&offset);
}
bool init()
{
  if(SDL_Init(SDL_INIT_EVERYTHING)==-1) {return false;}
  screen = SDL_SetVideoMode(scrWidth,scrHeight,scrBPP,SDL_SWSURFACE);
  if(screen==NULL){return false;}
  SDL_WM_SetCaption(title.c_str(),NULL);
 return true;
}
bool loadFiles()
{
  background = loadImage("Images/background.jpg"); 
  if(background==NULL){return false;}
  ball = loadImage("Images/ball.bmp");
  if(ball==NULL){return false;}
  platform = loadImage("Images/platform1.bmp");
  if(platform==NULL){return false;}
  brick1 = loadImage("Images/brick1.bmp");
  brick2 = loadImage("Images/brick2.bmp");
  brick3 = loadImage("Images/brick3.bmp");
  brick4 = loadImage("Images/brick4.bmp"); 
  if((brick1==NULL)||(brick2==NULL)||
     (brick3==NULL)||(brick4==NULL))
  {return false;}
  return true;
}
void cleanUp()
{
  SDL_FreeSurface(background); 
  SDL_FreeSurface(ball);
  SDL_FreeSurface(platform);
  SDL_FreeSurface(brick1);
  SDL_FreeSurface(brick2);
  SDL_FreeSurface(brick3);
  SDL_FreeSurface(brick4); 
  SDL_Quit();
}
Topic archived. No new replies allowed.