Site hosted by Angelfire.com: Build your free website today!

Building a Better Breakout

Or "How To Program One Of Those Games With The Silly Little Bouncing Ball That Goes Around And Breaks Those Blocks"

by Chris Roddy

Over the summer, I wrote my first TI-83 program (well, besides "Hello World" and all of that kind of stuff), which is a Breakout clone called Bonk. As I discovered ASM, I discovered also that my game moved about as fast as a NAFTA-protected delivery truck. Through the process of rewriting the game, I discovered some things which can help potential TI-BASIC (I'll just call it TB from now on) programmers in their endeavours toward the sacred moving pixel.

Let me preface this with some warnings:

  1. The coordinate systems in TB are rather retarded. They're different for graphics, graphing, and array commands. Some are Y,X and some are X,Y. Line( and Pxl-On( use different coordinate systems. You'll have to do a lot of footwork converting between all three.

  2. This article assumes a fair knowledge on the part of the reader of TB programming and of Algebra I, which I'm sure you'll have no trouble with.

  3. I used a lot of terrible programming techiniques in here... for example, Returns within If/Thens and such. NEVER, EVER jump a loop or an If/Then. It's ok to jump a plain old If, though. Also, NEVER use the Menu( statement. That jumps loops too. Jumping loops will slow your program to a halt and fill up the memory. Luckily these errors in Bonk were not at critical locations so it works fine.

  4. The bits of source code on this page are NOT in the public domain.

  5. Well, uh, there is no number 5. Sorry.

I. Ball Motion

The best way to start is to simply set up a program which bounces a ball around the screen. Don't worry about variable ball angles at this point. In fact, if you're writing in TB, don't even come within miles of a variable ball angle. The TB interface is just not fast enough. As for multi-pixel balls, your best bet is one of the different point types in Pt-On( and Pt-Off(. I used Pxl-On( because it seemed faster and less flickery to my eye (and other reasons, which you'll see later.)

The way I did it is I set up one program which coordinates the four directions the ball can move and calls the appropriate subroutine. That looks like this:

Repeat U=0  ' Loop until a sub returns 0 for U, indicating the end of a
If U=1      ' level or no more lives
prgmBONK1
If U=2      ' Subroutine calls
prgmBONK2
If U=3
prgmBONK3
If U=4
prgmBONK4
If Z=0      ' If lives=0 then they lost the game
Then
0\->\W
prgmBONKZ      ' Run the endgame sub
End
End
End
Where U is the quadrant the ball moves toward assuming it's at 0,0. (Huh?) It just contains the direction of the ball's motion. Each sub sets the new direction upon collision with a wall (which at this point can be determined by X and Y values and not by collision tests, which I will cover shortly.)

Each BONKx subroutine is nearly the same thing, except oriented in a different direction of motion. This takes up more memory than a more dynamic main engine, but it is much faster. By "hardwiring" the ball motion routines and eliminating slooooow if/thens, you speed up the game immensely.

Another thing I'll add is this: Move your ball two pixels at a time. I did that and it doubled the speed. No one really notices anyways.

[Please Pause Your VCR And Complete Step 1]

Now that you've got a pixel bouncing around the screen, debug it and get ready for...

II. Blocks

Take a break from the hard stuff for now and let's look at array->screen translations. What that refers to is the converting of array or list data into squares on the screen. The way I did it was rather stupid; I set up lists and scrolled through them like GWBASIC "data" statements.

A better technique is to nest two For(...End loops (one for column, the other for row) and have it do stuff based on the value of [A](Y,X... for example, draw a box, a dot, or some stuff inside the box. [B], which is the array Bonk uses contains only four different values: 0 for empty, 1 for an open box, 2 for a double and 3 for a triple block. It subtracts from these each time a block is hit. The number indicates how many more times the block must be hit to disappear.

The quickest way I've found to finish a level is this: Set up a variable which holds the number of blocks left on the screen. Build it up when you load the data and subtract from it as the blocks are broken. When it reaches 0, the level is completed.

Now that your program draws blocks:

III. Collision Tests

This is the most critical part of the entire program. If you screw up here, the game will be awful. Good collision tests (that is, the code which tests whether the ball is running into a wall, the paddle, or a block) can make or break your game. If your ball is sliding between blocks and bouncing wrong, it'll look terrible.

Collision tests with walls are easy. Just test the X or Y coordinate of the ball and change the direction accordingly.

Collision tests with the paddle are fairly easy. Just test the Y coord, and use a compound inequality (though you have to break it up into two for use with If) to determine if they caught the ball.

Collision tests with blocks are the reason I gave up on this three times before forcing myself to finish. A collision test with a block does three things:

  1. Figures out when the ball is touching an object.
  2. Erases that object if necessary.
  3. Bounces the ball.

There are two approaches to this, which I call "ball-position array cell approximation" and "pixel-test interperetation". Both of these are fairly easy to understand and not too herculan to implement.

The first technique takes the position of the ball and translates it (by using int( and other things) into a cell position in the block array. Based upon its direction and position, the program does different things to the screen and to the array. This is great if you want to put forty-three if/thens in your main game engine. You don't want to do that on a machine with a 6Mhz Z80 processor and a slooow BASIC interpereter. I believe that Bill Nagel's original Turbo Breakout used this techinique, but I may be wrong. Keep in mind that that was written in ASM. Speed like that with this technique is not possible in TB, IMHO.

Instead, use pixel-test interperetation. Test the pixel that the ball will occupy in its next move and decide what it is: is it part of a block, paddle, or wall? Then erase the block, update the array and bounce on your merry way. This is another reason to use Pxl- instead of Pt-. There is no "Pt-Test(" statement and you don't want to do that much coordinate translation footwork in your main game engine. Frank Force's TI-82 game "Breakout II" used this technique and it worked very well.

Here's the code for BONK1, one of the subs that did all of this in Bonk:

Repeat U\!=\1  ' Loop until we bounce
Pxl-On(Y,X     ' Draw the ball
getKey         ' See if we have to move the paddle
If Ans
prgmBONKP    ' Run the paddle motion sub
If pxl-Test(Y,X+1   ' Something to the right?...
Then
prgmBONKR    ' See what it is and act accordingly
Return
End
If pxl-Test(Y-2,X   ' Something above the ball? ...
Then
prgmBONKU         ' Act on it
Return
End
If pxl-Test(Y-2,X+2  ' Diagonal?...
Then
prgmBONKD1   ' Do something about it
Return
End
X+2\->\X         ' Update the ball position
Y-2\->\Y
Pxl-Off(Y+2,X-2   ' Erase the ball
End
Return
(Note that the ball is on for the optimum amount of time, and that while the calculations are occurring it stays lit. This avoids flickering.)

IV. Features

After you've laid the skeleton for the game, you're ready to add features. These are the easiest part. Things like save and restore, score, extra balls, and paddle motion all fit right into the game you've started. This is the part that's up to you and the part that makes each version of these games unique. Some things, however, like falling powerups cannot be added easily at this stage, since they would be controlled by the main game engine. Good planning of features will also make or break your game.

Enjoy...

All material on this page Copyright © 1997 by Chris Roddy. All Rights Reserved.