WRITING PONG GAMES IN BASIC

One of the most popular uses for color graphic animation today is for “Pong*”-like computer games. Probably everyone has seen a Pong game before. In fact, last year over 2 million tv games that featured Pong-like action were sold. In a game of Pong, a moving spot, or “ball,” glides and rebounds inside a fixed playing field, occasionally striking objects, such as “bricks,” and often one or more user-controlled bars, called “paddles.” The object of this game is usually to accumulate points, either within a specified time, or to a given limit. There can be one to four players, all playing separately, or competing against each other.

The floating-point BASIC of the Apple is especially suited for writing Pong-like games. The shape tables allow the ball to be any object you wish—a football, frisbee, or golfball, while the paddles can be tennis rackets, baseball bats, or putters. With the rotation commands we can make the ball spin in flight, and with the scale commands we can make it grow larger or shrink while gliding across the screen.

In this section we will explore how to program the Apple to play these games.

The Playfield

The main action of the game takes place on the playfield, which on the Apple in the high-resolution mode is a 280 by 160 matrix of colored dots, with a four-line text scrolling window immediately below the graphics matrix. Each dot in the matrix can be one of three colors (white, green, or violet) or can be black.

There are four basic parts to a playfield: the background, the paddle(s), the ball, and (sometimes) several rows or patterns of bricks. There can also be sidewalls bordering the playfield.

To begin the Pong game the HGR statement is used to switch the computer into the high-resolution graphics mode and clear the screen to all black. The scrolling window at the bottom four lines is used for displaying scoring information.

* PONG is a registered trademark of Atari, Inc.

Now the background must be drawn. If the color of the background is to be black, then this step can be skipped. If, however, it is to be another color, then these statements should be executed:

HCOLOR = C: HPLOT 0,0: CALL 62454

assuming C is the desired background color. This fills the entire playfield with color C by using an internal high-resolution assembly routine.

Balls

The major part of any Pong-like game is the ball, usually a moving spot which reflects, rebounds, and oscillates around and through the playfield. In the high-resolution mode we could make the ball a single tiny dot, but it would be very difficult to see as it moved. A better approach is to use a shape table to hold a figure that represents the ball. In this example we could use the shapes presented earlier in the mandala computer art programs, which consist of a square and a simple cross shape. By using two shapes we could have the program switch balls in midgame.

The first thing then is to POKE the game ball shapes into memory as we did in the mandala demonstration program:

1 HIMEM:8046
2 GOTO 1000
1000 REM LOADS THE TABLE START ADDRESS
1005 POKE 232,0: POKE 233,31: REM (8046)
1010 REM LOAD THE ACTUAL TABLE DATA
1015 FOR LOC = 8046 TO 8061: READ BYTE: POKE LOC,BYTE: NEXT BYTE: GOTO 10
1050 DATA 2,0,6,0,9,0,44,62,0,44,46,62,62,60,44,0

The basic action involved in moving a ball across the playfield is called an update loop. In flowchart form a typical update loop looks like that in Fig. 3-29.

(figure)

Fig. 3-29. Update loop.

Drawing and erasing the ball in the high-resolution graphics mode are accomplished by use of the DRAW and XDRAW statements. DRAW 1 AT X, Y will draw the ball shape (or any shape defined as shape number 1) at the location specified by the ordered pair (X, Y). XDRAW 1 AT X, Y will erase the same ball, without erasing any object underneath it. This means you can have the ball pass “over” objects in the playfield without erasing them.

Each time the program runs through the update loop it must calculate the new values for XNOW and YNOW (the new location of the ball). This is called “updating,” and is accomplished by adding a pair of “velocities” to the old X and old Y values. The velocities determine the angle and the speed of the moving ball. The X velocity determines the speed with which the ball moves back and forth, while the Y velocity determines the vertical deflection speed with respect to the X axis. If the Y velocity is zero, then the ball moves in a straight horizontal line; if the X velocity is zero, then the ball will move directly up and down. The sign ( + or -) of the velocity determines the direction of travel (- for up or left, + for down or right) while the magnitude (absolute value) determines the speed.

Velocities and Angles

Considering the previous ideas, it should be apparent that to move the ball at a shallow angle, the X velocity must be larger than the Y velocity (larger in magnitude, not sign). The reason for this is if the X and Y velocities are equal the ball will move at approximately 45°. Increase the Y velocity and the angle is larger; increase the X velocity and the angle is reduced. The actual angle of travel is a function of the Y velocity divided by the X velocity.

Obviously a Pong game would not be very much fun with only angles of 45° and greater; nor would it be too entertaining if the X velocity was always above 10 (20 is considered fast). Moreover, in Applesoft floating-point BASIC if the X velocity is much less than 1, the ball will move much too slowly. So we will make the game to use velocities between 1 and 20 maximum.

Programming the Update Loop

Due to the nature of most BASIC interpreters, the speed of the update loop is a function of its physical location in the body of the program. If the “draw new ball” step in the flowchart loops back to “calculate new ball location” by using a GOTO statement then the BASIC interpreter (particularly in the case of Microsoft BASIC derivations) will search through each line in the program until it finds the right line number of the “calculate new location” step. The further away this step is from the beginning of the program, the longer the search will be; if it is very close to the beginning of the program, then the search will be shorter. The major factor in ball speed is the execution time of the update loop. Thus to obtain maximum playing speed, put the update loop as close as possible to the beginning of the program.

Now using these ideas from the last three sections, here is a sample “update loop” ball moving program.

10 HGR: HCOLOR = 3: XOLD = 140: YOLD = 70
12 SCALE = 10: ROT = 0: SHAPE = 1: S = 1
14 XVEL = 15: YVEL = 10
20 XNOW = XOLD + XVEL: YNOW = YOLD + YVEL: XDRAW SHAPE AT XOLD, YOLD: DRAW SHAPE AT XNOW, YNOW: XOLD = XNOW: YOLD = YNOW: GOTO 20

This program will start a white ball shape (SHAPE = 1 gives shape number 1) at coordinates (140,70) and move it to the right at velocity 15 and downwards at velocity 10. Note the statements in line 20 that move XNOW and YNOW into XOLD and YOLD. These statements make the new ball the old ball, and enable the loop to cycle the ball to another position.

Bouncing

The only problem with our update loop is that as soon as the ball reaches the edge of the screen it will cause a syntax error. This is because the X and Y values will go beyond the allowed values of 279 or 159. The solution is to bounce the ball when it reaches the playfield edge.

Bouncing basically consists of complementing the sign of one or the other of the pair of velocities. Which velocity to complement is dependent upon which wall the ball is bouncing off. Also, the X and Y values must be readjusted after a bounce to be back in bounds. The key to bouncing is the IF…THEN statement, which is used to check if the ball is outside of the legal boundaries and to correct the ball if so. Fig. 3–30 is the flowchart for a four-wall bounce update loop:

Here is the listing for the new update loop with wallbounce:

20 XNOW = XOLD + XVEL: YNOW = YOLD + YVEL
30 IF XNOW < 10 OR XNOW > 268 THEN XVEL = -XVEL: XNOW = XOLD
40 IF YNOW < 10 OR YNOW > 148 THEN YVEL = -YVEL: YNOW = YOLD
50 XDRAW SHAPE AT XOLD, YOLD: DRAW SHAPE AT XNOW, YNOW: XOLD = XNOW: YOLD = YNOW: GOTO 20

(figure)

Fig. 3-30. Flowchart for four-wall bounce update loop.

We use the OR operator to check the maximum and minimum values of XNOW and YNOW. We used 10 and 268 for the X limits and 10 and 148 for the Y limits. This will keep the ball from wrapping around the screen to the opposite side. The value of XNOW and YNOW must be set to the XOLD and YOLD if either velocity complements. This is to get the ball back in the legal playfield area.

Paddles

A paddle is usually a vertical line or bar that is about one-fifth to one-eighth the vertical height of the playfield. The paddle is usually drawn at the extreme left or right border. The paddle can be another shape in the current shape table. Fig. 3–31 shows how a new paddle shape is created from 12 shape vectors and then converted to decimal values to put in the DATA statement.

On the Apple the PDL function returns a value between 0 and 255 which is proportional to the setting of a potentiometer connected to the Apple game i/o connector. Up to four pots can be attached and accessed by BASIC. The pots are numbered 0 to 3 and the function PDL(1), for example, will return a number from 0 to 255 proportional to the setting of pot No. 1.

(figure)

Fig. 3-31. Creating paddle shape and converting it to decimal values.

The values returned by the paddles are used for the vertical position of the paddle drawn on the screen. Since the vertical range of the outer boundary of the playfield is only 0 to 159 we must constrict the range of the paddle. This can be done by dividing the paddle value by the constant 1.6.

Also, since the SCALE for the paddle may differ from that of the ball, and since we might want to change the ROTation of the ball at some point, we should set the SCALE and ROT parameters before and after the paddle update code is executed.

Animating the paddle using PDL and the DRAW statement is basically the same as the update loop method for moving balls, and we can insert the new paddle code right inside the ball update loop. Place the new statements after the ball position is calculated, like this:

13 PADDLE = 3: POLD = PDL(1)/1.6
20 begin ball update loop
...
...
24 ROT = 0: SCALE = 5
25 PNOW = PDL(1)/1.6: XDRAW PADDLE AT 0, POLD: DRAW PADDLE AT 0, PNOW: POLD = PNOW
26 ROT = R: SCALE = S
...
...
ball update loop continued
...
...

In this program the variable R holds the rotation that was in effect before the paddles were updated, and likewise S holds the scale that was in effect before the paddles were updated. Statement 13 initializes the paddle's position POLD the first time the program is run.

We can use several methods for bouncing balls off paddles. The simple way is to simply check the ball's vertical position (YNOW) against the paddle's (PNOW) when the ball has an XNOW value of less than 10 or greater than 268. Another way is to implement a special type of bouncing where the angle of deflection is varied depending upon where the ball hits the paddle. This generates varying angles and makes the game more interesting.

Here is a listing of the game up to this point. Running it now will allow the ball to bounce endlessly inside the court. The paddle can be moved up and down to get its feel.

1 HIMEM : 8046
2 GOTO 1000
10 HGR: HCOLOR = 3 : XOLD = 140 : YOLD = 70
13 BALL = 1 : CROSS = 2 : PADDLE = 3 : SHAPE = BALL : S = 10 : SCALE = S : R = 0: ROT = R : XVEL = 20 : YVEL = 10
14 POLD = PDL(1)/1.6 : INC = 2
15 XDRAW SHAPE AT XOLD, YOLD
20 XNOW = XOLD + XVEL : YNOW = YOLD + YVEL
24 ROT = 0 : SCALE = 5
25 PNOW = PDL(1)/1.6: XDRAW PADDLE AT 0, POLD : DRAW PADDLE AT 0, PNOW : POLD = PNOW
26 ROT = R : SCALE = S
30 IF XNOW < 10 OR XNOW > 268 THEN XVEL = - XVEL : XNOW = XOLD
40 IF YNOW < 10 OR YNOW > 148 THEN YVEL = - YVEL : YNOW = YOLD
50 XDRAW SHAPE AT XOLD, YOLD : DRAW SHAPE AT XNOW, YNOW : GOTO 20
1000 REM LOADS THE TABLE START ADDRESS
1005 POKE 232, 0 : POKE 233, 31 : REM (8046)
1010 REM LOAD THE ACTUAL TABLE DATA
1015 FOR LOC = 8046 TO 8070 : READ BYTE : POKE LOC, BYTE : NEXT BYTE : GOTO 10
1050 DATA 3, 0, 8, 0, 11, 0, 18, 44, 62, 0, 44, 46, 62, 62, 60, 44, 0, 53,54, 54, 39, 36, 36, 0

Spinning and Expanding the Ball

Since the balls for this Pong game are made of shapes we can use the SCALE and ROT statements of the Apple. ROTation can be used to make the ball seem to spin as it glides across the screen. SCALE can make the ball grow larger or shrink smaller in the middle of flight.

For demonstration purposes we will modify the program so that rotation is incremented as the ball moves. Replace statement 50 with this:

50 XDRAW SHAPE AT XOLD, YOLD : R = R + INC : ROT = R : IF R > 63 THEN R = 0: DRAW SHAPE AT XNOW, YNOW : XOLD = XNOW : YOLD = YNOW

To make the size of the ball change on a random basis we can add a statement 60 that normally loops back to draw a new ball but 3 percent of the time allows a new scale for the ball to be generated. We will also generate new random values for the X and Y velocities, so the ball will change speed and direction when its scale is changed:

60 IF RND(1) < .97 THEN 20
65 XDRAW SHAPE AT XNOW, YNOW: S = RND(1) * 20 + 1: SCALE = S: DRAW SHAPE AT XNOW, YNOW: XVEL = RND(1) * 20 + 1: YVEL = RND(1) * 20 + 1
70 GOTO 20

Here is a listing of the entire program as it now stands:

1 HIMEM:8046
2 GOTO 1000
10 HGR: HCOLOR = 3: XOLD = 140: YOLD = 70
13 BALL = 1: CROSS = 2: PADDLE = 3: SHAPE = BALL: S = 10: SCALE = S: R = 0: ROT = R: XVEL = 20: YVEL = 10
14 POLD = PDL(1)/1.6: INC = 2
15 XDRAW SHAPE AT XOLD, YOLD
20 XNOW = XOLD + XVEL: YNOW = YOLD + YVEL
24 ROT = 0: SCALE = 5
25 PNOW = PDL(1)/1.6: XDRAW PADDLE AT 0, POLD: DRAW PADDLE AT 0, PNOW: POLD = PNOW
26 ROT = R: SCALE = S
30 IF XNOW < 10 OR XNOW > 268 THEN XVEL = -XVEL: XNOW = XOLD
40 IF YNOW < 10 OR YNOW > 148 THEN YVEL = -YVEL: YNOW = YOLD
50 XDRAW SHAPE AT XOLD, YOLD: R = R + INC: ROT = R: IF R > 63 THEN R = 0: DRAW SHAPE AT XNOW, YNOW:
60 IF RND(1) < .97 THEN 20
65 XDRAW SHAPE AT XNOW, YNOW: S = RND(1) * 20 = 1: SCALE = S: DRAW SHAPE AT XNOW, YNOW: XVEL = RND(1) * 20 + 1: YVEL = RND(1) * 20 + 1
70 GOTO 20
1000 REM LOADS THE TABLE START ADDRESS
1005 POKE 232,0: POKE 233,31: REM (8046)
1010 REM LOAD THE ACTUAL TABLE DATA
1015 FOR LOC = 8046 TO 8070: READ BYTE: POKE LOC, BYTE: NEXT BYTE: GOTO 10
1050 DATA 3, 0, 8, 0, 11, 0, 18, 44, 62, 0, 44, 46, 62, 62, 60, 44, 0, 53, 54, 54, 39, 36, 36, 0

Noises (Audio Output)

Most tv games that offer Pong-like play have sound effects, like the clicking sound of the ball hitting the wall, or a buzz or beep sound indicating a paddle miss. In the Apple noises can be generated from BASIC using a built-in speaker. Here is how.

Referencing location C030 hex (-16336 decimal) with a PEEK or POKE statement from BASIC will cause one-half cycle of a square wave to be sent to the speaker. Repeated references with PEEK or POKE will generate a tone with a frequency and duration determined by the frequency and number of references to the speaker. To produce a long, low buzz use this:

10 FOR I = 1 TO 100: SOUND = PEEK(-16336): NEXT I

To generate a short, high-pitched click from the speaker use this:

20 CLICK = PEEK(-16336) + PEEK(-16336) + PEEK(-16336) + PEEK(-16336) + PEEK(-16336) + PEEK(-16336) + PEEK(-16336) + PEEK(-16336) + PEEK(-16336) + PEEK(-16336) + PEEK(-16336) + PEEK(-16336)

Keep in mind that the ball and paddle movement are halted during any kind of audio output (the Apple does not use built-in timer chips as the PET does), so the noises should be made as short as possible to avoid jerky movement of the ball or paddle.

Another way to generate a beep is to print a control-G (bell) using a PRINT statement. This, however, takes a lot of time and is only recommended for beeps when all the action has stopped.

Scorekeeping

A player's score can be increased each time the ball hits a brick, or when the opponent misses the ball, or any other desired condition. The score can be displayed while the game is being played. PRINT statements can be used to display the score in the lower four lines of text below the playfield. HTAB and VTAB functions can be used to automatically position the player's score in the text area, like this:

90 VTAB 22: HTAB 10: PRINT ASCORE;: HTAB 30: PRINT BSCORE;

Of course this assumes the text window area has been cleared first with a HOME statement.

Theory of Play

The first thing when compiling all the techniques covered in this section into a working Pong-like game is to describe in considerable detail what the game is to do, step by step. Then a flowchart must be written that includes all necessary steps (update loop, bounce effect, sound, paddles, initialize, etc.). Then the program can be written, following the flow-chart, to do all the things described.

The general flow of a typical Pong game goes like this:

  1. Draw playfield, initialize variables.
  2. Delay in a paddle update loop to allow players to get used to the feel of the paddles.
  3. Pick a random position for the ball in the center of the screen and a pair of velocities.
  4. Start the update loop. Update the paddles, update the ball. Determine if the ball is going to (a) bounce, (b) miss a paddle, or (c) hit an object. If it does none of these, then return to the update loop.
  5. If it hits an object or brick, then add points to the appropriate player's score, and erase the object.
  6. If it misses a player's paddle, then add a point to the other player's score and erase the ball. If the other player has not won, then go back to the paddle update delay (Step 2).
  7. If a player has won (has more than a fixed number of points) then acknowledge the win and end the game.

Noises and other special effects can be added at the proper places in the program.


Return to Table of Contents | Previous Section | Next Section