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 = √(dx²+dy²)
        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
\$\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;
    
\$\endgroup\$
2
  • \$\begingroup\$ Your randomization initializations should be put into an anonymous namespace” — why is that? \$\endgroup\$
    – You
    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\$
    – Jamal
    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).

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

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.