7
\$\begingroup\$

I have previously asked here for a review of my Pong game. Since then I have implemented most of the recommendations but I'm not sure I did so correctly, the result can be found on my github.

Would it be possible to get another review? Below is the whole source code of the game:

main.cpp

/*
 * Pong game
 * Author: Chafic Najjar <[email protected]>
 * Note: Origin of the coordinate system is the upper left corner of the screen
 */
#include "pong.hpp"
int main(int argc, char *argv[]) {
 Pong pong(argc, argv);
 pong.execute();
 return 0;
}

pong.hpp

/*
 * Pong class declaration
 */
#ifndef PONG_HPP
#define PONG_HPP
#include <SDL2/SDL.h> // SDL library
#include <SDL2/SDL_ttf.h> // SDL font library
#include <SDL2/SDL_mixer.h> // SDL sound library
#include <iostream>
class Ball;
class Paddle;
class Pong {
public:
 Pong(int argc, char *argv[]);
 /* Screen resolution */
 static const int SCREEN_WIDTH;
 static const int SCREEN_HEIGHT;
 /* Window and renderer */
 SDL_Window* window; // holds window properties
 SDL_Renderer* renderer; // holds rendering surface properties
 /* Game objects */
 Ball *ball;
 Paddle *left_paddle;
 Paddle *right_paddle;
 /* Sounds */
 Mix_Chunk *paddle_sound; // holds sound produced after ball collides with paddle
 Mix_Chunk *wall_sound; // holds sound produced after ball collides with wall
 Mix_Chunk *score_sound; // holds sound produced when updating score
 /* Controllers */
 enum Controllers { mouse, keyboard, joystick };
 Controllers controller;
 SDL_Joystick *gamepad; // holds joystick information
 int gamepad_direction; // gamepad direction
 int mouse_x, mouse_y; // mouse coordinates
 /* Fonts */
 std::string fonts[2]; // font names
 SDL_Color dark_font; // dark font color
 SDL_Color light_font; // light font color
 SDL_Texture* font_image_left_score; // holds text indicating player 1 score (left)
 SDL_Texture* font_image_right_score; // holds text indicating palyer 2 score (right)
 SDL_Texture* font_image_winner; // holds text indicating winner
 SDL_Texture* font_image_restart; // holds text suggesting to restart the game
 SDL_Texture* font_image_launch1; // holds first part of text suggesting to launch the ball
 SDL_Texture* font_image_launch2; // holds second part of text suggesting to launch the ball
 /* Scores */
 int left_score; 
 int right_score;
 bool left_score_changed; // indicates when rendering new score is necessary 
 bool right_score_changed; // indicates when rendering new score is necessary 
 /* Game states */
 bool exit; // true when player wants to exit game
 /* Main functions */
 void execute();
 void input();
 void update();
 void render();
 void clean_up();
};
#endif

pong.cpp

/*
 * Pong class definition
 */
#include "pong.hpp"
#include "ball.hpp"
#include "paddle.hpp"
#include "utilities.hpp"
/* Screen resolution */
const int Pong::SCREEN_WIDTH = 640;
const int Pong::SCREEN_HEIGHT = 480;
Pong::Pong(int argc, char *argv[]) {
 /* Initilize SDL */
 SDL_Init(SDL_INIT_EVERYTHING);
 SDL_ShowCursor(0); // don't show cursor
 /* Window and renderer */
 window = SDL_CreateWindow("Pong",
 SDL_WINDOWPOS_UNDEFINED, // centered window
 SDL_WINDOWPOS_UNDEFINED, // centered window
 SCREEN_WIDTH,
 SCREEN_HEIGHT,
 SDL_WINDOW_SHOWN);
 renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC );
 /* Game objects */
 ball = new Ball(SCREEN_WIDTH/2-ball->LENGTH/2, SCREEN_HEIGHT/2);
 left_paddle = new Paddle(40, SCREEN_HEIGHT/2-30);
 right_paddle = new Paddle(SCREEN_WIDTH-(40+Paddle::WIDTH), SCREEN_HEIGHT/2-30);
 /* Sounds */
 Mix_OpenAudio(22050, MIX_DEFAULT_FORMAT, 2, 1024); // initialize SDL_mixer
 paddle_sound = Mix_LoadWAV("resources/sounds/paddle_hit.wav"); // load paddle sound
 wall_sound = Mix_LoadWAV("resources/sounds/wall_hit.wav"); // load wall sound
 score_sound = Mix_LoadWAV("resources/sounds/score_update.wav"); // load score sound
 /* Controllers */
 if (argc == 2) { 
 if ( strcmp(argv[1], "keyboard") == 0 )
 controller = keyboard;
 else if ( strcmp(argv[1], "joystick") == 0 )
 controller = joystick;
 else
 controller = mouse;
 } else
 controller = mouse; // default controller
 if (controller == joystick) {
 printf("%i joysticks were found.\n\n", SDL_NumJoysticks() );
 printf("The names of the joysticks are:\n");
 gamepad = SDL_JoystickOpen(0); // give control to the first joystick
 for(int i = 0; i < SDL_NumJoysticks(); i++) 
 std::cout << "\t" << SDL_JoystickName(gamepad) << std::endl;
 SDL_JoystickEventState(SDL_ENABLE); // initialize joystick controller
 gamepad_direction = 0;
 }
 /* Fonts */
 TTF_Init(); // initialize font
 dark_font = {67, 68, 69}; // dark grey
 light_font = {187, 191, 194}; // light grey
 fonts[0] = "resources/fonts/Lato-Reg.TTF";
 fonts[1] = "resources/fonts/FFFFORWA.TTF";
 font_image_launch1 = renderText("Press SPA", fonts[0], light_font, 18, renderer);
 font_image_launch2 = renderText("CE to start", fonts[0], dark_font, 18, renderer);
 /* Scores */
 left_score = 0;
 right_score = 0;
 left_score_changed = true; // indicates when rendering new score is necessary 
 right_score_changed = true; // indicates when rendering new score is necessary 
 /* Game states */
 exit = false;
}
void Pong::execute() {
 while(!exit) {
 input();
 update();
 render();
 }
 clean_up();
}
void Pong::input() {
 //=== Handling events ===//
 SDL_Event event;
 while(SDL_PollEvent(&event)) {
 // Track mouse movement
 if (event.type == SDL_MOUSEMOTION)
 SDL_GetMouseState(&mouse_x, &mouse_y);
 // Clicking 'x' or pressing F4
 if (event.type == SDL_QUIT)
 exit = true;
 // Joystick direction controller moved
 if (event.type == SDL_JOYAXISMOTION) {
 // 32767
 // Up or down
 if (event.jaxis.axis == 1)
 gamepad_direction = event.jaxis.value/5461;
 }
 // Joystick action button pressed
 if (event.type == SDL_JOYBUTTONDOWN) 
 if (ball->status == ball->READY)
 ball->status = ball->LAUNCH;
 // Pressing a key
 if (event.type == SDL_KEYDOWN)
 switch(event.key.keysym.sym) {
 // Pressing ESC exits from the game
 case SDLK_ESCAPE:
 exit = true;
 break;
 // Pressing space will launch the ball if it isn't already launched
 case SDLK_SPACE:
 if (ball->status == ball->READY)
 ball->status = ball->LAUNCH;
 break;
 // Pressing F11 to toggle fullscreen
 case SDLK_F11:
 int flags = SDL_GetWindowFlags(window);
 if(flags & SDL_WINDOW_FULLSCREEN)
 SDL_SetWindowFullscreen(window, 0);
 else
 SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN);
 break;
 }
 }
}
// Update game values
void Pong::update() {
 //=======================//
 //=== Paddle movement ===//
 // Right paddle follows the player's mouse on the y-axis
 if (controller == mouse)
 right_paddle->set_y(mouse_y);
 // Right paddle follows the player's gamepad
 else if (controller == joystick)
 right_paddle->add_to_y(gamepad_direction);
 // AI paddle movement
 left_paddle->AI(ball);
 //===================//
 //=== Launch ball ===//
 if (ball->status == ball->READY)
 return;
 else if (ball->status == ball->LAUNCH) {
 ball->launch_ball(left_paddle);
 ball->predicted_y = left_paddle->predict(ball);
 }
 //=========================//
 //=== Update ball speed ===//
 ball->update_speed();
 //=================//
 //=== Collision ===//
 if (ball->collides_with(left_paddle)) {
 ball->bounces_off(left_paddle);
 Mix_PlayChannel(-1, paddle_sound, 0); // play collision sound
 }
 else if (ball->collides_with(right_paddle)) {
 ball->bounces_off(right_paddle);
 ball->predicted_y = left_paddle->predict(ball); // predict ball position on the y-axis
 Mix_PlayChannel(-1, paddle_sound, 0);
 }
 // Upper and bottom walls collision
 if (ball->wall_collision()) {
 ball->dy *= -1; // reverse ball direction on y-axis
 Mix_PlayChannel(-1, wall_sound, 0); // play collision sound
 }
 //===============================//
 //=== Update ball coordinates ===//
 ball->x += ball->dx;
 ball->y += ball->dy;
 //=====================//
 //=== Ball goes out ===//
 if (ball->x > SCREEN_WIDTH || ball->x < 0) {
 // Change score
 if (ball->x > SCREEN_WIDTH) {
 left_score++;
 left_score_changed = true;
 } else {
 right_score++;
 right_score_changed = true;
 }
 Mix_PlayChannel(-1, score_sound, 0); 
 ball->reset();
 }
}
// Render objects on screen
void Pong::render() {
 // Clear screen (background color)
 SDL_SetRenderDrawColor( renderer, 67, 68, 69, 255 ); // dark grey
 SDL_RenderClear(renderer);
 // Color left background with light grey
 SDL_Rect left_background = { SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT };
 SDL_SetRenderDrawColor( renderer, 187, 191, 194, 255 );
 SDL_RenderFillRect( renderer, &left_background );
 // Paddle color
 SDL_SetRenderDrawColor( renderer, 212, 120, 102, 255 );
 // Render filled paddle
 SDL_Rect paddle1 = { left_paddle->get_x(), left_paddle->get_y(), Paddle::WIDTH, Paddle::HEIGHT };
 SDL_RenderFillRect( renderer, &paddle1 );
 // Render filled paddle
 SDL_Rect paddle2 = { right_paddle->get_x(), right_paddle->get_y(), Paddle::WIDTH, Paddle::HEIGHT };
 SDL_RenderFillRect( renderer, &paddle2 );
 // Render ball
 SDL_Rect pong_ball = { ball->x, ball->y, ball->LENGTH, ball->LENGTH };
 SDL_RenderFillRect(renderer, &pong_ball);
 // Render scores
 if (left_score_changed) {
 font_image_left_score = renderText(std::to_string(left_score), "resources/fonts/Lato-Reg.TTF", light_font, 24, renderer);
 left_score_changed = false;
 if (left_score == 5) {
 font_image_winner = renderText("Player 1 won!", fonts[0], light_font, 24, renderer);
 font_image_restart = renderText("Press SPACE to restart", fonts[0], light_font, 18, renderer);
 }
 }
 renderTexture(font_image_left_score, renderer, SCREEN_WIDTH * 4 / 10, SCREEN_HEIGHT / 12);
 int score_font_size = 24;
 if (right_score_changed) {
 font_image_right_score = renderText(std::to_string(right_score), "resources/fonts/Lato-Reg.TTF", dark_font, score_font_size, renderer);
 right_score_changed = false;
 if (right_score == 5) {
 font_image_winner = renderText("Player 2 won!", fonts[0], dark_font, 24, renderer);
 font_image_restart = renderText("Press SPACE to restart", fonts[0], dark_font, 18, renderer);
 }
 }
 renderTexture(font_image_right_score, renderer, SCREEN_WIDTH * 6 / 10 - score_font_size/2, SCREEN_HEIGHT/ 12);
 // Render text indicating the winner
 if (left_score == 5) {
 renderTexture(font_image_winner, renderer, SCREEN_WIDTH * 1 / 10 + 3, SCREEN_HEIGHT / 4); // align with score
 renderTexture(font_image_restart, renderer, SCREEN_WIDTH * 1 / 10 + 3, SCREEN_HEIGHT / 3);
 if (ball->status == ball->LAUNCHED) {
 left_score = 0;
 right_score = 0;
 left_score_changed = true;
 right_score_changed = true;
 }
 } else if (right_score == 5) {
 renderTexture(font_image_winner, renderer, SCREEN_WIDTH * 6 / 10 - score_font_size/2, SCREEN_HEIGHT / 4); // align with score
 renderTexture(font_image_restart, renderer, SCREEN_WIDTH * 6 / 10 - score_font_size/2, SCREEN_HEIGHT / 3);
 if (ball->status == ball->LAUNCHED) {
 left_score = 0;
 right_score = 0;
 left_score_changed = true;
 right_score_changed = true;
 }
 }
 // Draw "Press SPACE to start"
 else if (!ball->status == ball->LAUNCHED) {
 renderTexture(font_image_launch1, renderer, SCREEN_WIDTH / 2 - 80, SCREEN_HEIGHT - 25);
 renderTexture(font_image_launch2, renderer, SCREEN_WIDTH / 2 + 1, SCREEN_HEIGHT - 25);
 }
 // Swap buffers
 SDL_RenderPresent(renderer);
}
//=== Release resources ===//
void Pong::clean_up() {
 // Destroy textures
 SDL_DestroyTexture(font_image_left_score);
 SDL_DestroyTexture(font_image_right_score);
 // Free the sound effects
 Mix_FreeChunk(paddle_sound);
 Mix_FreeChunk(wall_sound);
 Mix_FreeChunk(score_sound);
 // Quit SDL_mixer
 Mix_CloseAudio();
 // Close joystick
 if (controller == joystick)
 SDL_JoystickClose(gamepad);
 // Destroy renderer and window
 SDL_DestroyRenderer(renderer);
 SDL_DestroyWindow(window);
 // Shuts down SDL
 SDL_Quit();
}

paddle.hpp

/*
 * Paddle class declaration
 */
#ifndef PADDLE_HPP
#define PADDLE_HPP
class Ball;
class Paddle {
private:
 // Paddle position
 int x;
 int y;
public:
 Paddle(int x, int y);
public:
 // Paddle dimensions
 static const int HEIGHT;
 static const int WIDTH;
 // Functions
 int get_x();
 int get_y();
 void set_y(int new_y);
 void add_to_y(int new_y);
 int predict(Ball *ball);
 void AI(Ball *ball);
};
#endif

paddle.cpp

/*
 * Paddle class definitions
 */
#include "paddle.hpp"
#include "pong.hpp"
#include "ball.hpp"
const int Paddle::HEIGHT = 60;
const int Paddle::WIDTH = 10;
Paddle::Paddle(int new_x, int new_y){
 x = new_x;
 y = new_y;
}
int Paddle::get_x() {
 return x;
}
int Paddle::get_y() {
 return y;
}
void Paddle::set_y(int new_y) {
 y = new_y;
 // Paddle shouldn't be allowed to go above or below the screen
 if (y < 0)
 y = 0;
 else if (y + HEIGHT > Pong::SCREEN_HEIGHT)
 y = Pong::SCREEN_HEIGHT - HEIGHT;
}
void Paddle::add_to_y(int new_y) {
 y += new_y;
 // Paddle shouldn't be allowed to go above or below the screen
 if (y < 0)
 y = 0;
 else if (y + HEIGHT > Pong::SCREEN_HEIGHT)
 y = Pong::SCREEN_HEIGHT - HEIGHT; 
}
// Imprecise prediction of ball position on the y-axis
int Paddle::predict(Ball *ball) {
 // Find slope
 float slope = (float)(ball->y - ball->y+ball->dy)/(ball->x - ball->x+ball->dx);
 // Distance between ball and paddle
 int paddle_distance = ball->x - x;
 // Prediction without taking into consideration upper and bottom wall collisions
 int predicted_y = abs(slope * -(paddle_distance) + ball->y);
 // Calculate number of reflexions
 int number_of_reflexions = predicted_y / Pong::SCREEN_HEIGHT;
 // Predictions taking into consideration upper and bottom wall collisions
 if (number_of_reflexions % 2 == 0) // Even number of reflexions
 predicted_y = predicted_y % Pong::SCREEN_HEIGHT;
 else // Odd number of reflexsion
 predicted_y = Pong::SCREEN_HEIGHT - (predicted_y % Pong::SCREEN_HEIGHT);
 return predicted_y; 
}
// Basic AI movement
void Paddle::AI(Ball *ball) {
 // Ball on the left 3/5th side of the screen and going left
 if (ball->x < Pong::SCREEN_WIDTH*3/5 && ball->dx < 0) {
 // Follow the ball
 if (y + (HEIGHT - ball->LENGTH)/2 < ball->predicted_y-2)
 add_to_y(ball->speed/8 * 5);
 else if (y + (HEIGHT - ball->LENGTH)/2 > ball->predicted_y+2)
 add_to_y( -(ball->speed/8 * 5) );
 }
 // Ball is anywhere on the screen but going right
 else if (ball->dx >= 0) {
 // Left paddle slowly moves to the center
 if (y + HEIGHT / 2 < Pong::SCREEN_HEIGHT/2)
 add_to_y(2);
 else if (y + HEIGHT / 2 > Pong::SCREEN_HEIGHT/2) 
 add_to_y(-2);
 }
}

ball.hpp

/*
 * Ball class declaration
 */
#ifndef BALL_HPP
#define BALL_HPP
class Paddle;
class Ball {
 public:
 Ball(int x, int y);
 // Ball status
 enum Status {READY, LAUNCH, LAUNCHED};
 Status status;
 // Ball dimensions
 static const int LENGTH;
 // Ball position
 int x;
 int y;
 // Ball movement
 int dx; // movement in pixels over the x-axis for the next frame (speed on the x-axis)
 int dy; // movement in pixels over the y-axis for the next frame (speed on the y-axis)
 bool bounce; // true when next frame renders ball after collision impact (ball has bounced)
 int speed; // ball speed = √(dx2+dy2)
 float angle; // angle after collision with paddle
 int hits; // counts the number of hits of the ball with the right paddle, increase speed after 3 hits
 int predicted_y; // predicted ball position on y-axis after right paddle collision (used for paddle AI)
 void launch_ball(Paddle *ai_paddle);
 void update_speed();
 bool wall_collision();
 bool collides_with(Paddle *paddle);
 void bounces_off(Paddle *paddle);
 void reset();
};
#endif

ball.cpp

/*
 * Ball class definitions
 */
#include "ball.hpp"
#include "pong.hpp"
#include "paddle.hpp"
#include <random>
std::random_device rd;
std::mt19937 gen(rd());
// Ball dimensions
const int Ball::LENGTH = 10;
Ball::Ball(int x, int y) {
 // Ball status
 status = READY;
 // Ball position
 this->x = x;
 this->y = y;
 // Ball movement
 dx = 0;
 dy = 0;
 bounce = false;
 speed = 8;
 angle = 0.0f;
 hits = 0; 
 predicted_y = 0;
}
void Ball::launch_ball(Paddle *ai_paddle) {
 std::uniform_int_distribution<int> dir(0, 1);
 int direction = 1+(-2)*(dir(gen)%2); // either 1 or -1
 std::uniform_int_distribution<int> ang(-60, 60);
 angle = ang(gen); // between -60 and 60
 dx = direction*speed*cos(angle*M_PI/180.0f); // speed on the x-axis
 dy = speed*sin(angle*M_PI/180.0f); // speed on the y-axis
 status = LAUNCHED;
}
void Ball::bounces_off(Paddle *paddle) {
 if (paddle == nullptr)
 return;
 hits++; 
 int sign;
 if (paddle->get_x() < Pong::SCREEN_WIDTH/2)
 sign = 1;
 else
 sign = -1;
 int relative_y = (y - paddle->get_y() + LENGTH);
 angle = (2.14f * relative_y - 75.0f);
 dx = sign*speed*cos(angle*M_PI/180.0f); // convert angle to radian, find its cos() and multiply by the speed
 dy = speed*sin(angle*M_PI/180.0f); // convert angle to radina, find its sin() and multiply by the speed
}
void Ball::update_speed() {
 // Increment ball speed for every 6 hits 
 if (hits == 5) {
 speed++;
 hits = 0;
 }
}
bool Ball::wall_collision() {
 return (y + dy < 0) || (y + LENGTH + dy >= Pong::SCREEN_HEIGHT);
}
bool Ball::collides_with(Paddle *paddle) {
 // left paddle
 if (paddle->get_x() < Pong::SCREEN_WIDTH/2) {
 // Check if collision with left paddle occurs in next frame
 if ( x > paddle->get_x() + Paddle::WIDTH )
 return false;
 else if (x < paddle->get_x())
 return false;
 else if (!(y + LENGTH > paddle->get_y() && y <= paddle->get_y() + Paddle::HEIGHT))
 return false;
 else
 return true;
 }
 // right paddle
 else {
 // Check if collision with right paddle occurs in next frame
 if ( x + LENGTH < paddle->get_x() )
 return false;
 else if (x > paddle->get_x() + Paddle::WIDTH)
 return false;
 else if (!(y + LENGTH > paddle->get_y() && y <= paddle->get_y() + Paddle::HEIGHT))
 return false;
 else
 return true;
 }
}
// Reset ball to initial state
void Ball::reset() {
 x = Pong::SCREEN_WIDTH/2 - LENGTH/2;
 y = Pong::SCREEN_HEIGHT/2;
 // Ball is fixed
 dx = 0;
 dy = 0;
 status = READY;
 // Speed and hit counter are reset to their initial positions
 speed = 8;
 hits = 0;
}

utilities.hpp

/* 
 * Useful functions
 */
#ifndef UTILITIES_HPP
#define UTILITIES_HPP
void renderTexture(SDL_Texture *tex, SDL_Renderer *ren, SDL_Rect dst, SDL_Rect *clip = nullptr) {
 SDL_RenderCopy(ren, tex, clip, &dst);
}
void renderTexture(SDL_Texture *tex, SDL_Renderer *ren, int x, int y, SDL_Rect *clip = nullptr) {
 SDL_Rect dst;
 dst.x = x;
 dst.y = y;
 if (clip != nullptr){
 dst.w = clip->w;
 dst.h = clip->h;
 }
 else
 SDL_QueryTexture(tex, nullptr, nullptr, &dst.w, &dst.h);
 renderTexture(tex, ren, dst, clip);
}
SDL_Texture* renderText(const std::string &message, const std::string &fontFile, SDL_Color color, int fontSize, SDL_Renderer *renderer) {
 TTF_Font *font = TTF_OpenFont(fontFile.c_str(), fontSize);
 SDL_Surface *surf = TTF_RenderText_Blended(font, message.c_str(), color);
 SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surf);
 SDL_FreeSurface(surf);
 TTF_CloseFont(font);
 return texture;
}
#endif
asked Apr 5, 2014 at 23:45
\$\endgroup\$
1
  • \$\begingroup\$ When running I show: error: 'abs' was not declared in this scope \$\endgroup\$ Commented Nov 11, 2017 at 6:40

2 Answers 2

6
\$\begingroup\$
  • You don't need return 0 at the end of main(). Reaching that point already implies a successful termination, so the compiler will insert it for you.

  • Prefer to avoid raw pointers and manual memory management whenever possible. Instead, utilize standard containers and C++11 smart pointers.

    With raw pointers as data members, you would have to maintain The Rule of Three (or The Rule of Five in C++11) because the provided copy constructor and assignment operator will only copy the pointers (shallow copy), not the data they point to (deep copy).

  • Utilize initializer lists for your classes:

    Ball:

    Ball::Ball(int x, int y)
     : status(READY)
     , x(x)
     , y(y)
     , bounce(false);
     , speed(8);
     , angle(0.0f);
     , hits(0); 
     , predicted_y(0)
    {}
    

    Paddle:

    Paddle::Paddle(int new_x, int new_y)
     : x(new_x)
     , y(new_y)
    {}
    

    This will especially be helpful in case you ever need to initialize const members.

  • Paddle's accessors should be const as they do not modify data members:

    int Paddle::get_x() const {
     return x;
    }
    
    int Paddle::get_y() const {
     return y;
    }
    
  • Your randomization initializations should be put into an anonymous namespace:

    namespace {
     std::random_device rd;
     std::mt19937 gen(rd());
    }
    

    This will prevent linker errors in case the same names are used in other files.

  • This:

    int sign;
    if (paddle->get_x() < Pong::SCREEN_WIDTH/2)
     sign = 1;
    else
     sign = -1;
    

    can become a single-line ternary statement:

    int sign = (paddle->get_x() < Pong::SCREEN_WIDTH/2) ? 1 : -1;
    
  • You're using sin() and cos(), but you haven't included <cmath>. I assume your compiler is being lenient for some reason and is not raising errors. You should still include this, as well as prefixing the functions with std::.

  • In collides_with(), I'd condense all the if/else if statements into one if, with each condition separated by a ||. Only one of them has to be met to return false.

    You'll then have something in this form (separate || lines may be necessary):

    if (condition1 || condition2 || condition3)
     return false;
    else
     return true;
    
answered Apr 6, 2014 at 0:38
\$\endgroup\$
2
  • \$\begingroup\$ "Your randomization initializations should be put into an anonymous namespace" — why is that? \$\endgroup\$ Commented Apr 6, 2014 at 7:47
  • \$\begingroup\$ @You: It was based on advice given to me here. I just haven't provided those details in this answer. I'll do that soon. \$\endgroup\$ Commented Apr 6, 2014 at 7:58
3
\$\begingroup\$

I just have a few additional comments.

First: (My main one) please, for the sake of all the furry little animals, your mother, and anything that's lovable in the world, don't hard-code the screen-width and screen-height as 640x480. Write your code to detect the screen size at run-time and act accordingly.

Second, I'd wrap the M_PI/180.0f in a constant, by strong preference inside a function:

template <class T>
deg2rad(double degrees) { 
 T factor = static_cast<T>(M_PI/180.0);
 return degrees * factor;
}

My other main suggestion would be to move more of the intelligence about the game out of pong::update and into the individual objects. For example, right now the code for handling a collision between the ball and a wall or paddle isn't handled by the ball or the wall or the paddle. IMO, it would be better if the ball and/or object it collided with handled the collision, rather than leaving it to "outside" code (though I'll openly admit that collisions between different types of objects is a problem can be difficult to keep completely object oriented).

answered Apr 6, 2014 at 1:44
\$\endgroup\$
1
  • \$\begingroup\$ I'm not sure I understood your first point, can you give an example? \$\endgroup\$ Commented Apr 8, 2014 at 14:59

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.