GLBasic User Manual

Main sections

13 The Second Game

PONG



PONG


Now that the basics are clear, the skills you've learnt must be strengthened and practiced. This second game is called "PONG". It is not only one of your first games in GLBasic, but it was also one of the first computer games of the world (1972 by Ralph H. Baer). If you are not familiar with PONG, you can read all about it here: http://www.pong-story.com

New Project


After the start of GLBasic select the button "new project" and give the project a name and an appropriate path for the project. e.g. ..\GLBasic\Projects\Pong

Variables



Now that the project is set up, the programming work can begin. We will need variables for the bat's x and y positions, as well as one for the score obtained so far. For all of these variables we will define a separate array so that 3, 4 or more players could easily be added later. This also allows us to reuse the code for bat movements and scoring in a loop.
More about this later...
Our code looks like this:

// Pong
DIM bat_y[2]
DIM bat_x[2]
DIM score[2]


Firstly we must initialize the players. To do this we add a subroutine (SUB) with the menu option: "project/new SUB". Give it the name "Init".

We need to write some code to draw the playing field and convert it into the BackBuffer so it will be drawn each frame. You could also load a picture rather than creating it by hand if you wanted to. Once this has been done we set the position of the racquets to the edge coordinates.

// ------------------------------------------------------------- //
// -=# INIT #=-
// ------------------------------------------------------------- //
SUB Init:
// Draw playfield, use as background bitmap
BLACKSCREEN
DRAWRECT 0,0, 640, 16, RGB(255, 255, 255)
DRAWRECT 0, 464, 640, 480, RGB(255, 255, 255)
DRAWRECT 312, 0, 16, 480, RGB(255, 255, 255)
USEASBMP

//Reset Bat Y Locations
bat_y[0]=240; bat_y[1]=240
// Reset Bat X Locations
bat_x[0]=16; bat_x[1]=600
ENDSUB // INIT


Reset the ball



At the beginning of each game, the ball must be placed in the center of the field and its speed must be set to 1. For this we create a new subroutine as described before and call it "ResetBall".

// ------------------------------------------------------------- //
// -=# RESETBALL #=-
// ------------------------------------------------------------- //
SUB ResetBall:
// Diese Variablen sind als LOCAL definiert:
// These variables are defined LOCAL:
// void
ball_x=320
ball_y=240

IF ball_sx<0
ball_sx=1
ELSE
ball_sx=-1
ENDIF

ball_sy=1
ENDSUB // RESETBALL


We need to call this function in Init, thus we add
GOSUB ResetBall
to the top of the subroutine "Init".

Viewing



We have typed a lot of code already, but still there is nothing to see. It is now time to show the playing field and the ball. For this we will create a new subroutine with the name ShowAll:

// ------------------------------------------------------------- //
// -=# SHOWALL #=-
// ------------------------------------------------------------- //
SUB ShowAll:
// Die Schläger anzeigen / Show the bats
FOR num=0 TO 1
DRAWRECT bat_x[num], bat_y[num], 16, 64, RGB(255, 255, 255)
PRINT score[num], num*320 + 32, 16
NEXT
// Der Ball / Draw the ball
DRAWRECT ball_x, ball_y, 16, 16, RGB(255, 255, 255)
SHOWSCREEN
ENDSUB // SHOWALL


Main Loop



Now we need a main loop. The main loop will call the game's other subroutines over and over again. Add the following lines above the first subroutine in the program. Code written above the first 'SUB' or 'FUNCTION' defines the main program. This main program will be executed when the program starts.

// pre-settings
GOSUB Init
MainLoop:
GOSUB ShowAll
GOTO Main


Beware! This code would cause the program to look like it was doing nothing if we did not call the instruction
SHOWSCREEN
in the ShowAll subroutine.

The First Run



You can now press the compile button and start the program. (F8, then F5)

Move!



The game now has a display but it is lacking movement. To fix this we will need a subroutine called 'MoveAll'. Before adding this though, we need to make sure our main loop will call the new MoveAll subroutine.
Before the following line in the main loop :
GOSUB ShowAll

add the following line
GOSUB MoveAll
in the main loop.

Now create a new subroutine called 'MoveAll' and add the following code :
// ------------------------------------------------------------- //
// -=# MOVEALL #=-
// ------------------------------------------------------------- //
SUB MoveAll:
// Paddles
FOR num=0 TO 1
// Keys: A, Z
IF KEY(30) THEN bat_y[0]=bat_y[0]-2
IF KEY(44) THEN bat_y[0]=bat_y[0]+2
// Keys: /\, \/
IF KEY(200) THEN bat_y[1]=bat_y[1]-2
IF KEY(208) THEN bat_y[1]=bat_y[1]+2

// Bat at upper/lower border?
IF bat_y[num]<0 THEN bat_y[num]=0
IF bat_y[num]>416 THEN bat_y[num]=416
NEXT


The appropriate codes for the KEY() command can be found by running the key code tool (under the menu option "tool/key codes").
Now again compile and start the program. With the keys "A, Z" and "up, down" you should be able to control the bats now.

Next, we move the ball based on its current x/y speed. Add this to the MoveAll subroutine :

// ball
ball_x=ball_x+ball_sx
ball_y=ball_y+ball_sy


Collision with Border



If the ball touches the ceiling or the floor, we simply reverse its y-speed:

// ball at bottom
IF ball_y>464
ball_y=464
ball_sy= -ball_sy
ENDIF
// ball at ceiling
IF ball_y<0
ball_y=0
ball_sy= -ball_sy
ENDIF


If the ball hits the left/right edge of the screen, a player scores one point and the ball is reset to the center again.

// ball left border - > score for player 1
IF ball_x<0
score[1]=score[1]+1
GOSUB ResetBall
ENDIF
// ball right border - > score for player 0
IF ball_x>624
score[0]=score[0]+1
GOSUB ResetBall
ENDIF


Collision with Bat



We loop for all players:
FOR num=0 TO 1


For each player we determine whether the ball is moving towards the bat. By doing this we ensure that the collision of the bat and ball is only checked - and hence processed - once for each bat. Without this, any time the ball hit a bat the ball's direction would change as many times as there were bats on the field.

IF (ball_sx<0 AND num = 0) OR (ball_sx>0 AND num=1) 


If the ball is moving towards the bat being processed then we examine whether the two rectangles for the ball and the bat overlap. If the ball touches a bat, it's x-speed is reversed (i.e. the ball bounces off the bat). The speeds in X and Y both also will increase with each bounce. We make the speeds change unequally as otherwise the angle of the ball's trajectory would never change.

col=BOXCOLL(bat_x[num], bat_y[num], 16, 64, ball_x, ball_y, 16, 16)
IF col=TRUE
// turn ball speed in X direction
ball_sx = -ball_sx
// speed up ball
ball_sx = ball_sx * 1.2
ball_sy = ball_sy * 1.05


Closing the IF and FOR statements:

  ENDIF
ENDIF
NEXT


Voila. The game is finished. Before writing your own game, it's important to ensure that you understand what each line of the code is doing.

Challenges


* Check the commands in the reference and try to extend the game from 2 to 4 players (put the bats at the top and bottom of the screen).

* Provide a computer player, so that you can play alone as well. As a hint, you only have to determine, whether the Y-coordinate of the ball is above or below the center of the computer's bat, and then move the bat accordingly

* Depending on the impact position of the ball on the bat, change the flight angle of the ball.

The program offers lot of freedom for your own ideas. Have fun playing with it.

The entire source code:

// Pong

DIM bat_y[2]
DIM bat_x[2]
DIM score[2]

// Voreinstellungen / Setup values
GOSUB Init

// Hauptschleife / Main Loop
main:
GOSUB MoveAll
GOSUB ShowAll
GOTO main



// ------------------------------------------------------------- //
// -=# INIT #=-
// ------------------------------------------------------------- //
SUB Init:
GOSUB ResetBall

// Spielfeld zeichnen, als Hintergrund nutzen
// Draw playfield, use as background bitmap
BLACKSCREEN
DRAWRECT 0, 0, 640, 16, RGB(255, 255, 255)
DRAWRECT 0, 464, 640, 16, RGB(255, 255, 255)
DRAWRECT 312, 0, 16, 480, RGB(255, 255, 255)
USEASBMP

// Schläger 0 setzten / Reset Bat 0
bat_y[0]=240; bat_y[1]=240
// Schläger 1 setzten / Reset Bat 1
bat_x[0]=16; bat_x[1]=600

// Klassischer Zeichensatz / Classic Font
LOADFONT "FontBW.bmp", 0
ENDSUB // INIT

// ------------------------------------------------------------- //
// -=# RESETBALL #=-
// ------------------------------------------------------------- //
SUB ResetBall:
// Diese Variablen sind als LOCAL definiert:
// These variables are defined LOCAL:
// void
ball_x=320
ball_y=240

IF ball_sx<0
ball_sx=1
ELSE
ball_sx=-1
ENDIF

ball_sy=1
ENDSUB // RESETBALL


// ------------------------------------------------------------- //
// -=# SHOWALL #=-
// ------------------------------------------------------------- //
SUB ShowAll:
// Die Schläger anzeigen / Show the bats
FOR num=0 TO 1
DRAWRECT bat_x[num], bat_y[num], 16, 64, RGB(255, 255, 255)
PRINT score[num], num*320 + 32, 16
NEXT
// Der Ball / The ball
DRAWRECT ball_x, ball_y, 16, 16, RGB(255, 255, 255)
SHOWSCREEN
ENDSUB // SHOWALL


// ------------------------------------------------------------- //
// -=# MOVEALL #=-
// ------------------------------------------------------------- //
SUB MoveAll:
// Paddles
FOR num=0 TO 1
// Tasten / Keys: A, Y
IF KEY(30) THEN bat_y[0]=bat_y[0]-2
IF KEY(44) THEN bat_y[0]=bat_y[0]+2
// Tasten / Keys: /\, \/
IF KEY(200) THEN bat_y[1]=bat_y[1]-2
IF KEY(208) THEN bat_y[1]=bat_y[1]+2

// Schläger am Rand? / Bat at border?
IF bat_y[num]<0 THEN bat_y[num]=0
IF bat_y[num]>416 THEN bat_y[num]=416
NEXT

// Ball
ball_x=ball_x+ball_sx
ball_y=ball_y+ball_sy

// Ball unten am Rand / Ball at lower border
IF ball_y>464
ball_y=464
ball_sy=-ball_sy
ENDIF

// Ball oben am Rand / Ball at upper border
IF ball_y<0
ball_y=0
ball_sy=-ball_sy
ENDIF

// Ball Linker Rand / Ball at left border
IF ball_x<0
score[1]=score[1]+1
GOSUB ResetBall
ENDIF

// Ball rechter Rand / Ball at right border
IF ball_x>624
score[0]=score[0]+1
GOSUB ResetBall
ENDIF

// Pong
FOR num=0 TO 1 // Für jeden Spieler / For each player
// Bewegt sich der Ball auf den Schläger 'num' zu?
// Does ball move toward bat 'num'
IF (ball_sx<0 AND num = 0) OR (ball_sx>0 AND num=1)
col=BOXCOLL(bat_x[num], bat_y[num], 16, 64, ball_x, ball_y, 16, 16)

IF col=TRUE
// Ballgeschwindigkeit in X umdrehen / Flip ball's X-direction
ball_sx= -ball_sx
// Schneller machen / Speed up
ball_sx = ball_sx * 1.2
ball_sy = ball_sy * 1.05
ENDIF
ENDIF
NEXT

ENDSUB // MOVEALL

See also...