How to make a Pong Game, by Sheraz Mahmood

Ok first off we are trying to make pong (using DJGPP and ALLEGRO), this is going to be a 2 player game (2 paddles). How does this game work will we have a ball which is bouncing around on the screen, and you must stop it from going off your side of the screen using a paddle. To start off we have to make the paddles and the ball for the game.

Ball graphic
BALL
Paddle graphic
PADDLE

Hay look there they are (you are going to have to convert these into bmp files for allegro to read them). If you already don't know this allegro can take files such as pictures, sounds, etc and put them in a nice neat file which can be used in a game or other things. To do this we have to load up GRABBER.EXE. Click on "Objects" now goto "NEW" then select RLE SPRITE. You should now be asked for a name put in "BALL". Now do that again and for the name now put "BAR". Now this time when you goto "NEW" choose PALLETE. Name this "PAL"

As you can probably see that everything that you have done has had a result on side list. Click on the first thing on the list (Should be the "BALL". Now goto "File" and select read BMP, now go find the file which has the picture of the BALL. When you have done that go back to "OBJECTS" and select GRAB. You should now select the size of the picture and it is done you now have your first sprite in the datafile. Now select the second item in the list should be "BAR". Now do what we just did with the BALL to the BAR. When you have done that select the PALLETE and now just choose "GRAB" form the "OBJECTS" menu. When you have done this in the place where it says "header" put down "pong.h". Now one last thing to do select "File" and then save. Save the file in the same place where you are going to have the source code.

If you have not gotten this, don't worry about it, the only reason that I did it was to show you how to use "GRABBER.EXE". Anyways I have done all of this for you and saved it in a zip file (plus the zip has the source code and the sound sample for the game.) which you can download at the end of the article.

STARTING OUT:

Load up RHIDE or another editor to use with DJGPP. I will not explain how to use RHIDE but will tell you how to add ALLEGRO into your porgrams in RHIDE. Ok we have to make a new project. Select "PROJECT" now select "Open Project". Goto where the GRABBER DATA file is and put in "pong" in the file name and click ok. Now you have a new project. Now click on "Options" then "libraries", put in "ALLEG" in the first spot and select the button beside it then click on "OK". Now select "PROJECT" and then "add item". Put in "pong.c", and now select "OK". There ladies and gental men we have our first project.

ON WITH THE GAME:

You should now be in the editor window with the "pong.c" file loaded. We need to first start off by including the files that are required for the game.

   /*Used for the standerd input/output functions*/
   #include <stdio.h>
   #include <stdlib.h>
   
   /*Allegro function header file*/
   #include <allegro.h>
   
   /*Pong header file*/
   #include "pong.h"

You are probably wondering what the "pong.h" file is for. Well when you created (or not) the datafile in the grabber utility it created a header file to store all the information of the datafile. We will be using this header file to get the data off of the datafile.Now the main function. If you type this all up and then execute it, it would run the program then exit (most likely you will not see much). Now lets load up the pong datafile. To start off we first have to make a global variable for all the other functions to use
   int main(void)
   {
      /*Init Allegro equipment*/
      allegro_init();
   
      /*Install the timer*/
      install_timer();
   
      /*Install the keyboard*/
      install_keyboard();
   
      /*Initizlise the joystick*/
      initialise_joystick();
   
      /*Install the mouse*/
      install_mouse();
   
      /*Set the sound system*/
      printf("Setting up the Sound.");
   
      if(install_sound(DIGI_AUTODETECT,MIDI_AUTODETECT,NULL) != 0) {
         /*Shutdown Allegro*/
         allegro_exit();
         /*Print error message*/
         printf("Error setting up Sound\n%s\n\n", allegro_error);
         exit(1);
      }
   
      /*Set the graphice mode*/
      printf("Setting up graphics mode 640x480.\n");
      
      if (set_gfx_mode(GFX_AUTODETECT, 640, 480, 0, 0) != 0) {
         /*Shutdown Allegro*/
         allegro_exit();
         /*Print error message*/
         printf("Error setting graphics mode\n%s\n\n", allegro_error);
         exit(1);
      }
      
      /*Shut down Allegro*/
      allegro_exit();
      
      return 0;
   }

There it is the main function. Well if you have not already guessed from reading the code that we have just Initizlised the allegro library, installed the timer function, installed the keyboard, initilized the joystick, installed the mouse, setup the sound card, set the graphics, and last of all exited the whole program.
      /*Get the Pong DataFile*/
      DATAFILE *pong_datafile=NULL;


This should be before the main function. Now lets open the datafile, this should go in the main function.

      .
      .
      .
      
      /*Install the mouse*/
      install_mouse();
      
      if((pong_datafile=load_datafile("pong.dat"))==NULL) {
         /*Shutdown Allegro*/
         allegro_exit();
         /*Print error message*/
         printf("Error loading \"pong.dat\"\n%s\n\n", allegro_error);
         exit(1);
      }
      
      .
      .
      .



What this basicly does is load the datafile "pong.dat" into the pointer variable "pong_datafile" then it checks it see if it was loaded proparly. If it was not then it exits ALLEGRO then prints out an error message and then exits the whole program.

Ok so far you shoud have this.

   #include <stdio.h>
   #include <stdlib.h>
   #include <allegro.h>
   
   /*Include the pong header file for the finding things in the datafile*/
   #include "pong.h"
   
   /*Get the Pong DataFile*/
   DATAFILE *pong_datafile=NULL;
   
   int main(void)
   {
      /*Init Allegro equipment*/
      allegro_init();
      
      /*Install the timer*/
      install_timer();
      
      /*Install the keyboard*/
      install_keyboard();
      
      /*Initizlise the joystick*/
      initialise_joystick();
      
      /*Install the mouse*/
      install_mouse();
      
      if((pong_datafile=load_datafile("pong.dat"))==NULL) {
         /*Shutdown Allegro*/
         allegro_exit();
         /*Print error message*/
         printf("Error loading \"pong.dat\"\n%s\n\n", allegro_error);
         exit(1);
      }
      /*Set the sound system*/
      printf("Setting up the Sound.");
      if(install_sound(DIGI_AUTODETECT,MIDI_AUTODETECT,NULL) != 0) {
         /*Shutdown Allegro*/
         allegro_exit();
         /*Print error message*/
         printf("Error setting up Sound\n%s\n\n", allegro_error);
         exit(1);
      
      }
      
      /*Set the graphice mode*/
      printf("Setting up graphics mode 640x480.\n");
      
      if (set_gfx_mode(GFX_AUTODETECT, 640, 480, 0, 0) != 0) {
         /*Shutdown Allegro*/
         allegro_exit();
         /*Print error message*/
         printf("Error setting graphics mode\n%s\n\n",
         allegro_error);
         exit(1);
      }
      
      /*Shut down Allegro*/
      allegro_exit();
      
      return 0;
   }


Now its time to set up the direction constants. These constants tell us the direction that the ball will be traviling.

      /*Direction consts*/
      #define DOWN_RIGHT   0
      #define UP_RIGHT     1
      #define DOWN_LEFT    2
      #define UP_LEFT      3

This should go after including the header files (at least that is where I like them to be). Basicly what these constants tell us is just the direction so if you were to pick direction 0 it would be DOWN_RIGHT, or direction UP_RIGHT which would be 1.

Now on to the direction variable, this variable is used to set the direction that the ball is traviling. Along with this I will set the x and y positions of the ball.


      int ball_x; //The location of the Ball
      int ball_y; //The location of the Ball
      int direction=0; //The direction the Ball is going

Now its time to setup the variables for the two paddles that the players will be using.


      int bar1_y=30; //Player 1 Bar position
      int bar2_y=430; //Player 2 Bar position


You might be wondering why I have bar1_y=30 and bar2_y=430. Well my reason is that the position of the player 1 bar will be in to the top most corner of the screen which is 30 (this is becasue the first 30 y pixels are taken to show the score). And player 2 will have its bar at the botton mose corner (the reason why this is not 430 because the bar is 50 pixels high and 430+50=480 which is the height of the screen.

Now onto the the score variables.


      int score_p1=0; //Player 1 score
      int score_p2=0; //Plater 2 score


This I belive is pretty self explanotary. The speed variables are next these variables are used to set the speed that the ball will be traviling, so lets set them.

      int speed=2; //The speed of the Ball
      int amout_of_hits=0; //Used to set the speed of the Ball

Ok the first variable "speed" is the used as the current speed and the speed that will be changed. Now the second variable "amount_of_hits" is used to set the speed of the ball, this is done by finding the remander of the amount of hits divided by 10. Now onto the graphics of the game.

Double Buffering:

Double buffering is a nice little technique which reduces flickering. Normally when you would write something you would display what ever you wanted to go onto the screen, this would cause the screen to flicker because when every you would display something you would have to clear the whole sreen then display it. But using double buffering what you would do is put everthing that you wanted to display on a virtual screen (a bitmap in the memory) and then copy that whole virtual screen to the acctual screen.

Lets create the double buffering feature for the game. Ok lets start off by creating the Virtual screen.

      BITMAP *buffer; //The screen buffer of double buffering

Ok so we have the virtual screen. Now what do we do, well we will have to create and clear the buffer. To do this all we have to do is put these lines after we load up the pallete in the main function:

      .
      .
      .
      
      /*Set the pallete for the sprites*/
      set_pallete(pong_datafile[pong_pal].dat);
      
      /*Clear and Create the buffer*/
      buffer=Create_bitmap(640,480);
      clear(buffer);
      
      .
      .
      .

Now lets place the ball in the center of the screen.

      .
      .
      .
      
      /*Set the pallete for the sprites*/
      set_pallete(pong_datafile[pong_pal].dat);
      
      /*Clear and Create the buffer*/
      buffer=Create_bitmap(640,480);
      clear(buffer);
      
      /*Set he Ball position*/
      ball_x=SCREEN_W/2;
      ball_y=35;
      
      .
      .
      .

Now we start off by creating a function called pong_game(void). What does this function do? It controls all that is going on in the game. Here it is:

   void pong_game(void)
   {
      /*Set the first random direction the ball will be traviling*/
      direction=random_direction();
      
      /*Play the game while the ESC key is not pressed*/
      while(!key[KEY_ESC]) {
         /*Move the Ball*/
         move_ball();
         /*Respond to the input*/
         key_respond();
         
         /*Put the information on the Screen*/
         textout(buffer,pong_datafile[pong_text].dat,"Player 1 Score:",
                 150,0,254);
         textout(buffer,pong_datafile[pong_text].dat,itoa(score_p1,NULL,10),
                 text_length(pong_datafile[pong_text].dat,
                 "Player 1 Score:")+150,0,10);
         textout(buffer,pong_datafile[pong_text].dat,"Player 2 Score:",
                 350,0,254);
         textout(buffer,pong_datafile[pong_text].dat,itoa(score_p2,NULL,10),
                 text_length(pong_datafile[pong_text].dat,
                             "Player 2 Score:")+350,0,10);
         textout(buffer,pong_datafile[pong_text].dat,"keyboard",0,0,255);
         textout(buffer,pong_datafile[pong_text].dat,"joystick",
                 640-text_length(pong_datafile[pong_text].dat,"joystick"),
                 0,255);
         /*Draw a line to set the boundries*/
         line(buffer,0,30,640,30,10);
         
         /*Put the buffer screen on the screen*/
         blit(buffer,screen,0,0,0,0,640,480);
         
         /*Clear the buffer*/
         clear(buffer);
      }
      return;
   }


Don't worry about the first part, I will explain all of that later. All I really want to explain is that it puts up the information about the users like the score and which side is for the joystick and which is the one for the keyboard. At the end the buffer is copyed to the screen and then the buffer is erased, note not the screen (this is what reduces flickering).

Ok what do we have now. Currently the program opens and then does all the graphics stuff and the sound stuff and the input devices stuff. After all of that is done we have to load up the pong_game function, we do that by putting the line pong_game(); in the main function, mainly after we set up the buffer and the ball position. So it goes like this:

      /*Set the pallete for the sprites*/
      set_pallete(pong_datafile[pong_pal].dat);
      
      /*Clear and Create the buffer*/
      buffer=create_bitmap(640,480);
      clear(buffer);
      
      /*Set the Ball position*/
      ball_x=SCREEN_W/2;
      ball_y=35;
      
      /*Play the Game*/
      pong_game();



Now it is time to put together the random_direction function. The purpose of this is to return a random direction that the balls will be traviling (you remember the direction constants that we put together before). Here it is:

   int random_direction(void)
   {
      return random()%3;
   }

That is not that hard. If you are wondering why is have random()%3; well the reason is that we have the direction from numbers 0 to 3 which is 4 numbers. So the random function will return a number and we will find a remainder which is mod by 3.

Well there is the random function now on to the boing function. What is this you may ask well this is the function that will play a music file (This music file is in the zip that I have along with this document). Here it is:

   void boing(void)
   {
      play_sample(pong_datafile[pong_boing].dat,255,255,1000,0);
      return;
   }


The play_sample function is involved with the allegro library. The play_sample function reads the sound sample from the datafile and then plays it in both speakers, then the second last argument means that it will be played at normal speed and the last argument tells us that it will not loop.

Now for the second last function it is called move_ball:

   void move_ball(void)
   {
      /*Set the speed according to the amout of hits*/
      if(amout_of_hits%10==0 && amout_of_hits!=0) {
         speed++;
         amout_of_hits++;
      }
   
      /*Respond to the direction the ball is traviling*/
      switch(direction) {
         case DOWN_RIGHT:
            ball_x+=speed;
            ball_y+=speed;
            if(ball_x>600) {
               if((ball_y>=bar2_y-20) &&( ball_y<=bar2_y+65)) {
                  direction=DOWN_LEFT;
                  amout_of_hits++;
                  boing();
               }
               else {
                  score_p1++;
                  ball_x=SCREEN_W/2;
                  ball_y=35;
                  direction=random_direction();
               }
            }
            else {
               if(ball_y>420)
               direction=UP_RIGHT;
            }
            break;
         case UP_LEFT:
            ball_x-=speed;
            ball_y-=speed;
            if(ball_x<5) {
               if((ball_y>=bar1_y-20) &&( ball_y<=bar1_y+65)) {
                  direction=UP_RIGHT;
                  amout_of_hits++;
                  boing();
               }
               else {
                  direction=random_direction();
                  score_p2++;
                  ball_x=SCREEN_W/2;
                  ball_y=35;
               }
            }
            else {
               if(ball_y<30)
               direction=DOWN_LEFT;
            }
            break;
         case UP_RIGHT:
            ball_x+=speed;
            ball_y-=speed;
            if(ball_x>600) {
               if((ball_y>=bar2_y-20) &&( ball_y<=bar2_y+65)) {
                  direction=UP_LEFT;
                  amout_of_hits++;
                  boing();
               }
               else {
                  score_p1++;
                  ball_x=SCREEN_W/2;
                  ball_y=35;
                  direction=random_direction();
               }
            }
            else if(ball_y<30)
               direction=DOWN_RIGHT;
            break;
         case DOWN_LEFT:
            ball_x-=speed;
            ball_y+=speed;
            if(ball_x<5) {
               if((ball_y>=bar1_y-20) &&( ball_y<=bar1_y+65)) {
                  direction=DOWN_RIGHT;
                  amout_of_hits++;
                  boing();
               }
               else {
                  score_p2++;
                  ball_x=SCREEN_W/2;
                  ball_y=35;
                  direction=random_direction();
               }
            }
            else {
               if(ball_y>420)
                  direction=UP_LEFT;
            }
      }
      /*Display the ball*/
      draw_rle_sprite(buffer,pong_datafile[pong_ball].dat,ball_x,ball_y);
      
      return;
   }

Move ball checks to see if the ball has gone over any of the users sides and then moves the ball according to the speed that is provided. At the end it adds 1 to the number of hits.
   void key_respond(void)
   {
      /*Update the joystick*/
      
      poll_joystick();
      
      /*Respond to the input*/
      if(key[KEY_DOWN])
         bar1_y+=6+up_ran_speed;
      
      if(key[KEY_UP])
         bar1_y-=6+up_ran_speed;
      
      if(bar1_y<=30)
         bar1_y=30;
      
      if(bar1_y>430)
         bar1_y=430;
      
      if(joy_down || key[KEY_Z])
         bar2_y+=6+up_ran_speed;
      
      if(joy_up || key[KEY_A])
         bar2_y-=6+up_ran_speed;
      
      if(bar2_y<=30)
         bar2_y=30;
      
      if(bar2_y>430)
         bar2_y=430;
      /*Display the bars on the screen*/
      draw_rle_sprite(buffer,pong_datafile[pong_bar].dat,0,bar1_y);
      draw_rle_sprite(buffer,pong_datafile[pong_bar].dat,635,bar2_y);
      
      return;
   }

This is the last function in the pong game. What it does it check to see what input the users have given to the players and then reacts to them. So if the users pushs the up arrow then the paddle on his side will go up or no move if it can not move anymore. I must also add that where the computer checks to see if the joystick up or down botton is pressed it also checks to see if the A and Z keys were pressed (in case you don't have a joystick).

Basicly when you put all these functions together you get the whole game. which can be found in this zip file.

PONG.ZIP (9541 bytes)

Author: Sheraz Mahmood