C++ Pong

Started by
10 comments, last by Cantos 16 years, 10 months ago
I made this pong game in allegro c++. It compiles and works well for my standards. I’m posting so all the great minds here can judge it and tell me what I did wrong and what can make it better. EDITED:: Right now I’m trying to do this. How can I make the ball bounce off the paddle with a more realistic feeling? For example I’m doing it with rand, shouldn’t the ball be affected by the direction the paddle was moving before it hit it? Thank you for your time. Main.cpp

#include "Pong.h"

int main() 
{
    allegro_init();
    install_keyboard();
                     
    Pong P;
    P.setGame();

    return 0;
}
END_OF_MAIN()


Pong.h

#ifndef H_Pong
#define H_Pong

#include <allegro.h>
#include <cstdlib>
#include <time.h>

class Pong
{
  public:     
   BITMAP *buffer; 
   time_t secs;      
         
   int ball_x;
   int ball_y;
   
   int ball_tempX;
   int ball_tempY;

   int AI_x;
   int AI_y;

   int AI_tempX;
   int AI_tempY;

   int p2_x;
   int p2_y;

   int p2_tempX;
   int p2_tempY;
         
   int speed; // for ball speed
   
   int hits;
   
   int AIScore;
   int p2Score;
   
   // Keeps track of circles direction
   // 1 = up and left, 2 = down and left, 3 = up and right, 4 = down and right              
   int dir; 

   void setGame();
   void startNew(); 
   void AIMove();      // Player 1
   void p2Move();      // Player 2
   void moveBall();    // Controls ball
   void checkWinner(); // Checks for winner and points scored 
   void menu();        // Updates score and hits

   // Constructor 
   Pong();
};
#endif


Pong.cpp

#include "Pong.h"

void Pong::setGame()
{
    ball_x = 400;
    ball_y = 240;
   
    ball_tempX = 400;
    ball_tempY = 240;

    AI_x = 31;
    AI_y = 255;

    AI_tempX = 31;
    AI_tempY = 255;

    p2_x = 753;
    p2_y = 255;
    
    p2_tempX = 753;
    p2_tempY = 255;

    speed = 1; 
   
    hits = 0;
   
    AIScore = 0;
    p2Score = 0;

    set_color_depth(16);
    set_gfx_mode( GFX_AUTODETECT, 800, 600, 0, 0);
    
    buffer = create_bitmap( 800, 600 ); 

    acquire_screen();
    // background
    clear_to_color ( buffer, makecol( 197, 219, 191 ));
           
    draw_sprite ( screen, buffer, 0, 0 );
    release_screen();
    
    time(&secs);
    srand( (unsigned int)secs);
    dir = rand() % 4 + 1;  
    
    // Check for key press to exit
    while( !key[KEY_ESC])
         {   
          AIMove();
          p2Move();
          moveBall();
          checkWinner();
         }      
}  

void Pong::startNew()
{
     // comment this later
    clear_keybuf();
    readkey();
    
    // background
    clear_to_color ( buffer, makecol ( 197, 219, 191 ));
    // resets ball back to the middle
    ball_x = 400;
    ball_y = 240;

    // resets paddles after victory.
    AI_x = 31;
    AI_y = 255;

    p2_x = 753;
    p2_y = 255;
    
    speed = 1;
    
    hits = 0;
}    

// Controls the ball
void Pong::moveBall()
{
    ball_tempX = ball_x;
    ball_tempY = ball_y;

   // Hits test for ball speed    
    if (hits >= 30)
       {
        speed = 5;
       }
    else 
        if (hits >= 20)
           {
            speed = 4;
           }   
         else 
             if (hits >= 10)
                {
                 speed = 3;
                }
             else 
                 if (hits >= 5)
                    {
                     speed = 2;
                    }

    if (( dir == 1 ) && ( ball_x > 5 )&& ( ball_y > 5 ))
       {
         if (( ball_x == AI_x + 15 ) && ( ball_y >= AI_y ) && ( ball_y <= AI_y + 60 ))
            {
              dir = rand() % 2 + 3;
              
              hits++;
            }
         else
            {    
             ball_x -= speed;
             ball_y -= speed;
            }       
       }
        
    else 
        if (( dir == 2 ) && ( ball_x > 5 ) && ( ball_y < 600 ))
           {
            if (( ball_x == AI_x + 15 ) && ( ball_y >= AI_y ) && ( ball_y <= AI_y + 60 ))
               {
                dir = rand()% 2 + 3;
                
                hits++;
               }
            else
               {    
                  ball_x -= speed;
                  ball_y += speed;
               }
            } 
            
        else 
            if (( dir == 3 ) && ( ball_x < 800 ) && ( ball_y > 5 ))
               {
                if(( ball_x + 5 == p2_x ) && ( ball_y >= p2_y )&& ( ball_y <= p2_y + 60 ))
                  {
                   dir = rand()% 2 + 1;
                   
                   hits++;
                  }
                else
                   {    
                    ball_x += speed;
                    ball_y -= speed;
                   }
                } 
                
            else 
                if ((dir == 4 ) && ( ball_x < 800 ) && ( ball_y < 600 ))
                   {
                    if(( ball_x + 5 == p2_x ) && ( ball_y >= p2_y ) && ( ball_y <= p2_y + 60 ))
                      {
                       dir = rand()% 2 + 1;
                       
                       hits++;
                      }
                    else
                        {   
                          ball_x += speed;
                          ball_y += speed;
                        }
                     } 
                     
                  else 
                     { 
                      if (( dir == 1 ) || ( dir == 3 ))    
                         {
                          ++dir;
                         }
                      else 
                          if (( dir == 2 ) || ( dir == 4 ))
                             { 
                              --dir;
                             }
                      }    
    
    acquire_screen();
    circlefill ( buffer, ball_tempX, ball_tempY, 5, makecol( 197, 219, 191 ));
    circlefill ( buffer, ball_x, ball_y, 5, makecol( 22, 136, 11));
    draw_sprite( screen, buffer, 0, 0);
    release_screen();
    
    // Updates menu
    menu();
    
    rest(5);
}    

// Player 1
void Pong::AIMove()
{
    AI_tempY = AI_y;
       
    //if on cpu's side of court
    if (( ball_x < 391 ) && ( AI_y > 0 ))
       { 
        // 391?? i could gmake it 400 but 
        //this makes it m,ore natural
        
        //attempt to position in the middle 
        if ( AI_y < 183 )
           {
            AI_y +=4;
           }
        
        if ( AI_y > 217 )
           {
            AI_y -= 4;
           }
       }
    
    //if ball is close to player
    if (( ball_x < 117 ) && ( AI_y < 540 ))
       {
        // attempt y-axis allignment
        if ( AI_y < ball_y )
        {
         AI_y += 10;
        }
        
        if ( AI_y > ball_y )
           {
            AI_y -= 10;
           }
       }
    
    acquire_screen();
    rectfill( buffer, AI_tempX, AI_tempY, AI_tempX + 10, AI_tempY + 60, makecol ( 197, 219, 191 ));
    rectfill( buffer, AI_x, AI_y, AI_x + 10, AI_y + 60, makecol ( 218, 173, 50 ));
    release_screen(); 
}  

// Player 2
void Pong::p2Move()
{
    p2_tempY = p2_y;
 
    if (( key[KEY_UP] ) && ( p2_y > 0 ))
       {
        //--p2_y;  
        p2_y -= 3;    
       }
    
    else 
        if (( key[KEY_DOWN] ) && ( p2_y < 540 ))
           {
            //++p2_y;  
            p2_y += 3;  
           }     
    
    acquire_screen();
    rectfill( buffer, p2_tempX, p2_tempY, p2_tempX + 10, p2_tempY + 60, makecol ( 197, 219, 191 ));
    // actual player 2
    rectfill( buffer, p2_x, p2_y, p2_x + 10, p2_y + 60, makecol ( 29, 98, 255));
    release_screen();      
}    

// Checks for winner
void Pong::checkWinner()
{
    if ( AIScore == 11 )
       {
        textprintf_centre_ex(screen, font, 400, 250, makecol(0,0,255),-1, "GAME OVER!!\n\n PLAYER 1 WINS!");
        clear_keybuf();
        readkey();
        AIScore = 0;
       }
        
    else
        if ( p2Score == 11 )
           {
            textprintf_centre_ex(screen, font, 400, 300, makecol(255,0,0),-1, "GAME OVER!!\n\n PLAYER 2 WINS!");
            clear_keybuf();
            readkey();
            p2Score = 0;
           }

    // Checks for goal score
    if ( ball_x > p2_x + 20 )
       {
        AIScore++; 
        startNew();
       }   
    else 
        if ( ball_x < AI_x - 20 )
           {
            p2Score++;
            startNew();
           }  
}    

void Pong::menu()
{
    textprintf_centre_ex(screen, font, 50, 10, makecol(218, 173, 50),-1, "Score: %d", AIScore);
    textprintf_centre_ex(screen, font, 750, 10, makecol(29, 98, 255),-1, "Score: %d", p2Score);
    textprintf_centre_ex(screen, font, 400, 10, makecol(22, 136, 11),-1, "Hits: %d", hits);
}

Pong::Pong()
{
}


[Edited by - neob on June 6, 2007 6:44:10 PM]
Advertisement
Looks very good![smile]

I do have three suggestions though:


  • Your constructor should initialize all membersof the class


  • Initializating members (Espically pointers) can dramtically reduce the number
    of potential problems.

  • You should try avoiding the use of "Magic Numbers"


  • In C++, you should use either enumerations or constants
    in place of numbers. For example:, instead of seeing:
        ball_x = 400;    ball_y = 240;

    Readers of your code can see:
        ball_x = SCREENX/2;    ball_y = SCREENY/2;

    SCREENX and SCREENY could be declared in the class like:
       const int SCREENX = 800;   const int SCREENY = 480;

    Because they are constants, the compilier can optomize the divide out of the
    code (So it effectivley just assigns the varables).

    Two benifits of doing this is to increase readbility, and make changes
    easier as changing SCREENX will change all code that indirectly refrences it.

  • Temporary varables shouldnt be not be a class member.


  • If you need to store the temporary varable for longer then a function call,
    make it static:
    void foo () {  static int i=0;  std::cout << i++ << std::endl;}// prints numbers 0 to 9for (int i=0; i<10; i++)   foo ();


    If you need the varable for a more important purpose, coinsider
    changing its name to reflect its primary purpose instead.


Good points I shall fix them, specially the constructor.

I have another question. How can I make the ball bounce off the paddle with a more realistic feeling? For example I’m doing it with rand, shouldn’t the ball be affected by the direction the paddle was moving before it hit it?
Yes it should. I'm not sure if trigonometry is involved or needed. But the easiest way to have the direction of the ball be determined by the movement of the paddle is to save the state of the paddle's movement.

Basically you save the the current position (IOW, where the paddle was when it made contact with the ball) and where its position was just before it hit the paddle. So if paddle.y = 10 (at contact) and paddle.y = 12 (just before contact), then you make the ball.y -=1 (IOW, move upwards).

I hope that was as clear as mud [grin]


*that's why n00bs shouldn't give advice. wrong was: +=1 and downwards

[Edited by - Alpha_ProgDes on June 6, 2007 3:46:53 PM]

Beginner in Game Development?  Read here. And read here.

 

I wouldn't recommend placing your initialization of variables in your constructor.

Instead have an initialize function which you call which just sets all the initial values. That way if you need to reset your class, rather then destroying it and creating a new one, you can just call the initialize function and Voila everything's been restarted.
So you are saying that I should:
original position of the paddle - position of contact

?
lol that mud has lots of detritus, I cant see well in it :)
Quote:Original post by neob
So you are saying that I should:
original position of the paddle - position of contact

?

yes.
Quote:lol that mud has lots of detritus, I cant see well in it :)


I would agree with you but I don't know what detritus is [smile]

Beginner in Game Development?  Read here. And read here.

 

Quote:Original post by Dancin_Fool
I wouldn't recommend placing your initialization of variables in your constructor.

Instead have an initialize function which you call which just sets all the initial values. That way if you need to reset your class, rather then destroying it and creating a new one, you can just call the initialize function and Voila everything's been restarted.


Dont listen to him, he is blaspheming! [grin]

Seriously, use constructors, they *guarentee* that you won't forget to call them.

If you need to reset it is easy, provided your class has a reasonable overload of operator=().
myObject = Object(); // voila, fresh as new!


In this example, the Pong class needs to either deep copying the BITMAP or use a smart pointer to it.

How often do you need to reset objects anyway?
I will use the constructor method, because after 11 points from either AI or P2 the game can close.

However the other method would be implemented as well. I have this method startNew() that after a new "goal" has been scored it resets some variables that need to be.
i have made pong before on the calculator(very slow >.<) and the method i did to make it more realistic...
i had two variables for the vertical and horizontal motion of the ball...
then i would add those to the current coordinates each time...
coordinates are x and y... and the speed is a and b...
x=x+a
y=y+b
my paddle was 3 pixels wide so it was pretty easy to do the collision detection... but anyways...
if it hit the top of the paddle...the speed would go +1... so the more times you hit the edge the faster it will go in that direction... and bottom change the speed -1 every time... and the middle would not do anything except change the direction... so... if the ball is coming fast down to the top of the paddle... it would not change the direction of the ball... but rather slow it down...

it seemed logical to me... and for however wide the paddle is you just have to scale changes in speed accordingly...

hope it helped

This topic is closed to new replies.

Advertisement