# C++ Pong

## Recommended Posts

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

// 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();

// background
clear_to_color ( buffer, makecol ( 197, 219, 191 ));
// resets ball back to the middle
ball_x = 400;
ball_y = 240;

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();

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();
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();
p2Score = 0;
}

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

{
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]

##### Share on other sites
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;

    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.

##### Share on other sites
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?

##### Share on other sites
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]

##### Share on other sites

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.

##### Share on other sites
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 :)

##### Share on other sites
Quote:
 Original post by neobSo 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]

##### Share on other sites
Quote:
 Original post by Dancin_FoolI 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?

##### Share on other sites
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.

##### Share on other sites
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

##### Share on other sites
Quote:
Original post by Alpha_ProgDes
Quote:
 Original post by neobSo 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]

But if the paddle is at y300 and the ball is hit somewhere around y600...
I really dont get it

##### Share on other sites
Hi

I finished my own pong clone a few weeks ago. I used c++ and Win32, and I did some things differently to yours. Maybe you would be interested in this. This function is called whenever a new ball is needed to continue play.

void ballObject::newBall(){	dead = false;	speed = 2.0;	xLoc = 320;	yLoc = 180;	xActual = xLoc;	yActual = yLoc;	switch(rand()%2)	{		case 0:			leftright = 1;			break;		case 1:			leftright = -1;			break;		default:			leftright = 1;	}	angle = (rand()%91) - 45;}

xLoc and yLoc are ints used when drawing the ball's position, xActual and yActual are doubles that get passed into some trigonometry later. leftright is an int that describes whether the ball is traveling left or right across the screen. If it's -1, then the X coordinate of the ball is decreasing, and if it's 1, then the X coordinate is increasing. angle is an int that is randomly generated as 45 to -45. A negative number in angle means the Y coordinate of the ball is decreasing, and a positive number means it's increasing.

This function gets called on every game tick, (100 times a second).
void ballObject::updatePosition(paddleObject* player1,paddleObject* player2){	double hypotenuse = speed;	double adjacent;	double opposite;	// find out how far the ball has travelled since the last tick	opposite = (sin(angle*PI/180) * hypotenuse);	adjacent = (cos(angle*PI/180) * hypotenuse);	xActual = xActual + (adjacent*leftright);	yActual = yActual + opposite;	// collision detection for walls	if (yActual < MINY ||		yActual+16 > MAXY)	{		// bounce noise!		PlaySound(MAKEINTRESOURCE(IDW_PLIK),hClassInst,SND_RESOURCE|SND_ASYNC);		// we hit a wall, so multiply the angle by -1		angle = angle * -1;	}	// collision detection for paddles	if (xActual < MINX)	{		// hitting player2 paddle?		if (yActual > player2->yLoc &&			yActual < player2->yLoc+120 &&			leftright == -1)		{			// bounce noise!			PlaySound(MAKEINTRESOURCE(IDW_PLIK),hClassInst,SND_RESOURCE|SND_ASYNC);			// we hit the left paddle, so X now has to count up			leftright = 1;			// increase speed!			speed = speed + 0.1;			// angle deflection if the paddle is moving when the ball bounces			if (player2->goingUp)			{				angle = angle -3;			}			if(player2->goingDown)			{				angle = angle +3;			}		}	}	if (xActual+16 > MAXX)	{		// hitting player 1 paddle?		if (yActual > player1->yLoc &&			yActual < (player1->yLoc+120) &&			leftright == 1)		{			// bounce noise!			PlaySound(MAKEINTRESOURCE(IDW_PLIK),hClassInst,SND_RESOURCE|SND_ASYNC);			// we hit the right paddle, so X now has to count down			leftright = -1;			// increase speed!			speed = speed + 0.1;			// angle deflection if the paddle is moving			if (player1->goingUp)			{				angle = angle -3;			}			if(player1->goingDown)			{				angle = angle +3;			}		}	}	// check if the ball has gone out of play	if (xActual < -16 ||		xActual > 640)	{		// it has, so set the ball to dead status		dead = true;		// award a point		if (xActual < -16) { player1->score++; }		if (xActual > 640) { player2->score++; }		if (player1->score > 2)		{			player1->wins = true;		}		if (player2->score > 2)		{			player2->wins = true;		}		// set the newball timer		SetTimer(hClassWnd,				IDT_NEWBALL,				3000, // after three seconds				(TIMERPROC)NULL);	}}

I hope you can get some inspiration from this.

## Create an account

Register a new account

• ### Forum Statistics

• Total Topics
627700
• Total Posts
2978690

• 20
• 14
• 12
• 10
• 12