10

h_rule.jpg

Detecting Collisions



Suppose a player fires a missile at an alien attacking space craft. Wouldn't it be nice to have a simple way to tell when there is a collision between the missile and alien ship? Or suppose you're making a maze game. Would you like to be able to detect when a player touches a maze wall?

COLLISION DETECTION REGISTERS

You can. Collision detection is easy with PMG! The Atari engineers wisely provided several collision detection registers for this purpose. Here they are:

Missile Collisions

Memory Location
53248
53249
53250
53251
53256
53257
53258
53259
Shows:
Missile 0 to playfield collision
Missile 1 to playfield collision
Missile 2 to playfield collision
Missile 3 to playfield collision
Missile 0 to player collision
Missile 1 to player collision
Missile 2 to player collision
Missile 3 to player collision

Player Collisions

Memory Location
53260
53261
53262
53263
53252
53253
53254
53255
Shows:
Player 0 to player collisions
Player 1 to player collisions
Player 2 to player collisions
Player 3 to player collisions
Player 0 to playfield
Player 1 to playfield
Player 2 to playfield
Player 3 to playfield

ASSIGNING VARIABLE NAMES

To simplify your programming task, I recommend that you assign a variable name to each collision register you need to use. It's a lot easier to keep track of variable names than all those memory locations.* Atari recommends variable names such as these:

M0PF (missile 0 to playfield collision) M1PF (missile 1 to playfield collision)

In the demonstration program in this chapter we will be using two collision registers: M0PF and P0PF.

ANSWER

 

1. 384   2. 128   3. a

 
Player 0 to playfield collision (53252) If you have the program from the previous chapter loaded into memory, you might want to initialize P0PF and M0PF right now by adding these statements to line 10070: P0PF=53252: M0PF=53248

*Later, though, you may wish to go back to using constants (such as 53260, 53261, etc.). That's because Atari BASIC only allows you 128 variable names. Also, statements with constants actually execute slightly faster than those using a corresponding variable! (This is not true with other computers such as the PET and Apple, where variables execute 10 to 40 times faster than constants.) I'd like to thank B. B. Garrett for clarifying this in his informative article "Atari Times" in the May, 1983 issue of Compute!

READING COLLISION REGISTERS

You can read a collision register with a peek command. For example:

COLLISION=PEEK(P0PF)

After this statement executes, COLLISION will contain either 0,1,2, or 4. In our sample program you will find that:

Ok, try this one. Suppose you draw a line on the screen with these statements:

GRAPHICS 7:SETCOLOR 2,3,4:COLOR 3:PLOT 0,0:DRAWTO 159,0

Furthermore, suppose you read a collision register like this:

COLLISION=PEEK(P0PF)
ANSWER

 

4 (a 4 shows a collision with a playfield drawn with COLOR 3).

 

MULTIPLE COLLISIONS

Once a collision occurs, the collision register retains the number that was placed in it. If a second collision occurs, the next number is added to the number that already exists there.

ANSWER

 

5 (the 1 from the first collision will be added to the 4 from the second collision).

 

CLEARING COLLISION REGISTERS

Since the collision registers retain the values put in them when a collision occurs, it's important to reset them. This is almost as easy as taking candy from a baby. All you do is poke a 1 into the HIT CLEAR register at location 53278.

I suggest you use a variable for the HIT CLEAR register. Let's call it HITCLR. At line 10070 insert this statement:

HITCLR=53278

Often we think of clearing something by poking zeros in it. But this is different; here we are turning on the hit clear switch. That's why we poke it with 1 rather than 0.

ANSWER

 

You're right if you said "a. All collision registers are cleared."

 

USING COLLISION REGISTERS

Often programmers peek at the collision registers and then use a series of IF-statements to decide on what action to take. But there's a much better way. Remember, in Atari BASIC you can GOSUB a variable or even GOSUB a PEEKED value! We did this in our joystick routine, and we can also do it with collision registers.

ANSWER

 

GOSUB PEEK(P0PF)+40

 

ANSWER

 

We need a subroutine at line 44. That's because P0PF will contain a 4 if player 0 hits a COLOR 3 playfield.

 

*In this book, a COLOR 1 playfield is simply a playfield drawn with COLOR 1. The term "playfield 1," as used in the Atari technical manual, is not synonymous with the term "COLOR 1 playfield."

DRAWING A PLAYFIELD

Now let's use some of these techniques in a PMG program. First, let's draw a playfield. We'll make it a maze so that in a later chapter we can expand the program into a full-fledged game.

We've already reserved lines 12000-13000 for drawing a playfield, so let's put our new playfield subroutine there. (Note that we need to delete the previous playfield, which was contained in lines 12000, 12010, 12020, and 12030.)

Here are the lines for the new playfield subroutine. I suggest you enter them now.

11999 REM DRAW PLAYFIELD
12000 SETCOLOR 2,3,4:COLOR 1 
12010 PLOT 0,0:DRAWTO 159,0:DRAWTO 159,79:DRAWTO 0,79:DRAWTO 0,0 
12015 COLOR 2 
12020 PLOT 80,61:DRAWTO 80,79:PLOT 0,60:DRAWTO 30,60:DRAWTO 30,79 
12025 COLOR 3 
12030 PLOT 52,6O:DRAWTO 52,45:DRAWTO 110,45:DRAWTO 110,60:DRAWTO
      137,60:DRAWTO 137,45:DRAWTO 110,45:PLOT 159,32 
12040 DRAWTO 110,32:PLOT 80,45:DRAWTO 80,20:PLOT 130,16:DRAWTO 130,14:
      DRAWTO 127,14:DRAWTO 127,16:DRAWTO 130,16 
12050 PLOT 43,0:DRAWTO 43,30:PLOT 52,45:DRAWTO 22,45:DRAWTO 22,13:PLOT
      103,0:DRAWTO 103,17:POKE 559,34:RETURN

Also, let's revise lines 2010 and 2015 so that line 2010 becomes 2015 and 2015 becomes 2010. It seems that PMG works better when the playfield is drawn before the PMG setup is executed.

And at line 2005 change GRAPHICS 5 to GRAPHICS 7 since our new playfield requires that graphics mode.

REVISING THE MAIN LOOP

Now let's revise the main loop of our program so that it detects when our player touches the sides of the walls. First, change line 200 into line 201. Do this so that we can create some new collision detection routines at line 200.

Now at the beginning of line 200, let's simply print the contents of the collision register--just so we can see what they contain when various objects collide. This is easy to do, like so:

? "P0PF=";PEEK (P0PF),"M0PF=";PEEK(M0PF)

Next, also at line 200, let's call for the execution of the two collision detection subroutines, one for P0PF and one for M0PF. And let's arrange things so that if there is no collision, we immediately return from each subroutine. We'll let the P0PF subroutine start at line 40 and the M0PF subroutine start at line 50.

Here's the call to the M0PF subroutine:

GOSUB PEEK(M0PF)+40:
ANSWER

 

GOSUB PEEK(P0PF)+50

 

Altogether then, line 200 will look like this:

200? "P0PF=";PEEK(P0PF),"M0PF=";PEEK(M0PF):GOSUB PEEK(M0PF)+40:GOSUB PEEK(P0PF)+50

PLAYER-PLAYFIELD COLLISIONS

Simple right? Now all we need to do is decide what we want to happen when a collision occurs. For now, let's fix things so that our player cannot walk through the maze walls. Here's how we'll do it. At the beginning of line 201 we'll save the X0 and Y0 coordinates by inserting the statement X0A=X0 and Y0A=Y0:

201 Y0A=Y0:X0A=X0:GOSUB PEEK(JOYSTICK):GOSUB MOVELEGS

Then when our player hits a maze wall, we'll reset X0 and Y0 back to what they were before the collision. Got it?

Suppose our player hits a COLOR 1 playfield. Then P0PF will contain a "1," and control will pass to line 41 (as a result of GOSUB PEEK(P0PF)+40). Then at line 41 all we need to do is:

1. Set the Y0 and X0 coordinates to what they were before the collision.

2. Clear the collision registers by poking a 1 into HITCLR.

ANSWER

 

41 X0=X0A:Y0=Y0A:POKE HITCLR,1:RETURN

 

We also need this same subroutine at line 42. That's because P0PF will contain a 2 if our player hits a COLOR 2 playfield.

If our player hits a COLOR 3 playfield, P0PF will contain a 4 (strange as this may seem).

ANSWER

 

44 (Since 40+4=44.)

 

ANSWER

 

44 X0=X0A:Y0=Y0A:POKE HITCLR,1:RETURN

 

Let's look at the playfield detection statement more closely. It is: GOSUB PEEK(M0PF+40).

ANSWER

 

Control will pass to line 40 (0+40=40).

 

ANSWER

 

All we need is a RETURN statement at line 40. This will return control to the main loop.

 

MISSILE COLLISIONS

The missile collision subroutines start at line 50 since the calling routine is GOSUB M0PF+50. If no missile-playfield collision has occurred, we won't need any specific action.

ANSWER

 

50 RETURN

 

If a missile hits a wall, we will want to do these things:

ANSWER

 

190 POKE HPOSM0,0:D1=0:FIRE0=0:POKE HITCLR,1:RETURN

 

ANSWER

 

51 (M0PF will contain 1.1+50=51)

 

ANSWER

 

We'll need line 52 and line 54. (Remember, if a missile hits playfield 3 then the collision register will contain a 4.)

 

Line 190 contains the commands that we want executed if a missile hits a wall so at line 52 all we need is:

52 GOSUB 190:RETURN

Similarly, at lines 53 and 54 we'll use the same code:

53 GOSUB 190:RETURN
54 GOSUB 190:RETURN

TRY IT

Make all the changes I've discussed so far to MISSILE.SAV. Also, change line 2 to:

SAVE"D:MISSCOL.SAV":STOP
All the changes are summarized in the listing that follows:

MISSCOL.BAS
Download (Saved BASIC)
Download / View (Listed BASIC)

Save the program and then run it. Note: I suggest you push the RESET button each time before you run the program; this will ensure that the PMG image appears correctly when the program first starts. As you move the player against the various walls of the maze, notice the values that appear in P0PF.

Next try firing the missile in various directions. Notice that sometimes the missile will hit a wall. When this happens, the missile sound stops and the missile disappears. The player can now immediately fire another missile.

Sometimes the missile will go right "through a wall." That's because the missile is moving in increments of 6. Actually, the missile is "hopping over the walls"--it really never touches the wall--hence there is no collision. This could be fixed by making the walls thicker or the missile bigger. Or the missile coordinates could be adjusted by 1 instead of 6. Of course, then the missile would probably move too slowly for most purposes.

Yet another approach would be to use an assembly language routine to move the missile.* In the programs in this book we'll simply let the missile pass through walls. In a game situation, this effect is good because you never know when one of your missiles will be "super-charged" and capable of passing through a wall.

*Watch for my next book on advanced PMG techniques. In it I'll show you how to use machine language subroutines to speed up your PMG programs.

SLOW PLAYER MOVEMENT

Notice that the player moves rather slowly. That's mainly because we are constantly peeking at and printing the values contained in the collision registers. You can speed up the player's movement considerably by deleting ?P0PF=";PEEK(P0PF) and ?M0PF=";PEEK(M0PF) in line 200.

Of course part of the reduction in the player's speed results because the main loop is becoming more complicated. Remember, we are doing quite a bit in this little BASIC program. We have created:

But with player-missile graphics, once we have come this far, it's easy to add even more "features."

ADDING MORE FEATURES

Let's try something a little different. Let's pretend that the COLOR 1 playfield has an electrical charge that will zap our player if he touches it. To produce the "zapping effect," let's produce a strange sound and flash several colors through the player when he touches playfield 1. Furthermore, let's set our player to a random new color and then put him back to his starting location. The code to do this is relatively simple and won't slow down the main action. That's because the code will not be in the main loop but in subroutines that will execute only when a collision occurs.

Here's the new code to "zap" the player when he touches the COLOR 1 playfield. Change line 41 to:

41 X0=175:Y0=80:GOSUB 400:POKE 704,PEEK(RANDOM):
PLAYER0$=BUFFER$:POKE HPOSP0,X0:POKE HITCLR,1:RETURN

And add lines 400 and 410:

400 FOR I=0 to 100 STEP 2:POKE 704,I:SOUND 0,I,10,15:SOUND 1,I+50,10,15:NEXT I
410 SOUND:RETURN

Also change line 499 to line 390 so that line 390 reads "GOTO 200."

As you can see the subroutine at line 400 rapidly moves different values into player 0's color register. This causes him to "glow." In the same loop we also include a nice sound effect with the aid of a couple of SOUND statements. (I used SOUND statements, here, rather than poking audio control registers, because SOUND statements are easier to use. Remember, speed is not so important here since we are not in the main loop. When a player is zapped, it's natural for the action to stop. All attention is focused on the zapped player, anyway.)

There you have it. Collision detection complete with a fancy routine to zap a player and move him back to his starting location if he hits a specific kind of playfield.

In the next chapter we'll pull together everything you've learned so far and create "MAZEDUEL," a racing game in which two players compete for a dangerous but valuable "crystal."


Return to Table of Contents | Previous Chapter | Next Chapter