Animating Your Player
So far you have learned to design a player image and carry out the necessary PMG setup routines needed to display it on the screen in any desired color. After finishing this chapter you will be able to move your player around on the screen with or without joystick control.
Animation of a player without joystick control is much easier to program, so let's start with that first.
ANIMATION WITHOUT A JOYSTICK
Once you have a PMG setup routine working, it's quite easy to produce different results with minor program changes. In the program in the previous chapter we established the position of our player with the statements "X0=175" and "Y0=75." We used X0 to specify the horizontal position and Y0 to specify the vertical. In our new animation program, we will still use these statements to tell where we want the player first positioned. But after that we will set up a loop in which we will either increase or decrease the values in X0 and Y0. (We will use X0 for the horizontal position of player 0 since "X" in mathematics traditionally refers to the horizontal plane. Similarly, "Y" usually refers to the vertical plane. So I use Y0 to control the vertical position of the player.)
Main Program Loop
Here is the start of the main loop of our new program.
- As you examine this code, see if you can tell which statement puts the program into a never-ending loop.
1 GOSUB 200:GOTO 190
200 POKE 53248,X0
230 GOTO 230
Note that because the program is looping, the horizontal and vertical positions of the player are constantly being set. With every pass through the loop we have the opportunity to change the player's position. For example, to move the player up the screen, all we need do is decrease the value in Y0. We can do this with the statement Y0=Y0-1. A better way, though, is to use the statement Y0=Y0-SP, where SP is a variable set to "1." This way you have more flexibility. You can easily set SP to a different value and the player will move more or less rapidly.
- Now, take a look at the code above for a moment and decide where we need to put Y0=Y0-SP to make the player move up the screen.
Important: again, watch out that you don't confuse X0 with XO. "XO" is "X zero." Similarly, "Y0" is "Y zero" not YO. Remember that a zero has a different shape than the letter "0."
Run the program. When you've got the player moving up and off the screen, continue reading below.
Error 5 What happened when the player disappeared off the screen? You should have gotten Error 5: "String Length Exceeded." I'll show you how to handle this problem later. For now, just know that this error is to be expected whenever your player moves too far up or down. It happens whenever Y0 is set to a number less than 1 or greater than 128. That's because PLAYER0$ is dimensioned to 128 bytes. On line 220 we are moving data into PLAYER0$ at the byte specified by Y0. Because there is no such thing as byte 0 for a string, a value of 0 in Y0 results in an error.
As soon as you get Error 5. try this: Print the value in Y0. (Type ? Y0 and press RETURN.) You will notice that Y0 contains "0." an illegal value!
- By now you may already know how to move the player horizontally. Assuming that you take out line 225, what statement would you need to put into the loop to make the player move horizontally across the screen from right to left.?
Try it. First insert "REM" at the beginning of line 225 so that line 225 will not be executed. Then insert X0=X0-SP, as line 227. When you run the program, the player should move from right to left across the screen. Again, don't worry about the error message that occurs when the player runs off the screen. This time you will get Error 3: "Bad Value." The bad value this time will be a -1 in X0. We'll handle this problem later.
Now let's try diagonal movement. First, let's position the player at the top left corner of the screen. We do this by changing lines 13000 and 13010 so that they read:
For the moment also change line 230 to "GOTO 230." When you run the program, the player will appear at the top left corner of the screen and just stay there.
- Now let's make him run diagonally. See if you can come up with the statements required to make that happen. The compare your code with mine.
- Now here's another one. Write the statements needed to move the player diagonally (as before). But when she reaches vertical position 45, move her horizontally from left to right until she reaches horizontal position 125, at which point just have her stop. Write the necessary code in the space provided. Then try it out and debug it as necessary. It may take a bit of experimenting to get it to work. Finally, compare your code with mine.
Note: Lines 200-227 could be combined into a single line. This would make the player move slightly faster. But to make these programs easier to read, I'm putting each statement on a separate line.
Well, moving a player without a joystick was fairly simple. Moving her with a joystick is not quite so easy.
To control the movement of a player with a joystick, you need to:
- Read the joystick.
- Figure out the position that the stick has been moved to.
- Change the value of X0 or Y0 accordingly.
Reading the stick is easy. For example, this statement will read joystick 0 (the one on the far left) and put a numerical value in "S:"
Note: The place where you plug in joystick 0 is labeled "Controller Jack 1." Similarly, "Controller Jack 2" is the label for the port for joystick 1, and so on.
This diagram shows the various numbers that will be read into "S" when the joystick is moved to each position.
- Suppose you move the joystick to the right. What will be the value of "S" after the statement S=STICK(0) is executed?
- What is the value of "S" when the joystick is not moved, but left in its normal resting position?
You can handle the values put into "S" in different ways. Here's one simple way:
IF S=7 THEN X0=X0+SP
IF S=11 THEN X0=X0-SP
IF S=14 THEN Y0=Y0-SP
IF S=13 THEN Y0=Y0+SP
IF S=6 THEN Y0=Y0-SP:X0=X0+SP
IF S=10 THEN Y0=Y0-SP:X0=X0-SP
IF S=9 THEN Y0=Y0+SP:X0=X0-SP
IF S=5 THEN Y0=Y0+SP:X0=X0+SP
This works. But look at all those IF-statements! They slow down program execution and make it difficult to get smooth and lively animation. Actually, you don't need to use any IF-statements at all. You don't even need the STICK statement.*
For maximum speed, it's better to peek into memory location 632 to read joystick 0. like this:
For joystick 1 we would use "PEEK(633)," for joystick 2. "PEEK(634)" and so on.
Now, to handle the joystick value without "IF-statements," we will simply use a GOSUB command. A powerful feature of Atari BASIC is that you can use a PEEK command in combination with a GOSUB statement.
- Suppose you pull straight back on joystick 0. What value will be in memory location 632? (Look at the joystick drawing if you need to.)
Now suppose we write this statement:
- To which subroutine will the program go if you pull back on the joystick?
*Thanks go to that wild and wacky game programmer, Robert Sombrio, for showing me the fast joystick reading routine presented in this chapter. I tested it against a machine language subroutine published in a major magazine. Robert's routine won hands down!
MODIFYING THE PROGRAM
Now let's modify our program again. At line 10050 let's initialize a variable called "JOYSTICK" to 632. And while we're at it, let's initialize HPOS0 to 53248, and SP to 1 also at line 10050. Then we can peek into "JOYSTICK" to determine which subroutine to execute. Line 10050 will then read:
Our main loop will then look like this:
200 GOSUB PEEK(JOYSTICK):POKE HPOSP0,X0:PLAYER0$(Y0)=IMAGE1$:GOTO 200
Now we also need something at lines 5, 6, 7, 9, 10, 11, 13, 14, and 15. Here are lines 5, 6, 7, 9, and 10:
- To complete the routine, we need to add lines 11, 13, and 14. See if you can write line 11:
- Now write the code for lines 13 and 14. (If location 632 contains 13. the joystick was moved down; if location 632 contains 14. the joystick was moved up.)
- How about line 15? If the joystick is not moved at all, location 632 will contain 15. In this case we don't want to change the X0 or Y0 values at all but simply return from our subroutine. Given that information see if you can write line 15.
To clean up the program, be sure to delete lines, 2, 190, 220, 225, 227, 230, 235, and 240 since they are not needed for joystick control.
Also, after deleting line 190, be sure to change line 1 to:
1 GOSUB 2000:GOTO 200
On the next page you will find a complete listing of the program for moving a player under joystick control. Try it out. When you've got it running, continue reading and we'll make a few more changes.
SETTING DISPLAY PRIORITIES
Another nice feature of PMG is that you can make players hide behind certain playfield objects, yet come out in front of others. This is called setting display priorities (or often simply "picking a priority option"). When you are programming in BASIC, you'll use memory location 623 as the priority register.* (This register is also used for other purposes, but one thing at a time!)
*Location 623, technically, is the shadow of hardware register 53275. A shadow is a RAM register as opposed to a ROM hardware register in an Atari chip like GTIA. Thirty times a second the operating system takes whatever value is in the shadow register and sticks it into the corresponding hardware register.
You can set various display priorities by poking either 1, 2, 4, or 8 into location 623. If you poke 1 into location 623, players will show up in front of all playfields. Furthermore, player 0 will appear in front of player 1, player 1 will appear in front of player 2, and so on.
If you poke 2 into location 623, then players 2 and 3 will appear behind all playfield objects. Players 0 and 1 will still appear in front of all playfield objects.
If you poke 4 into location 623, then all playfields will appear in front of all players.
If you poke 8 into location 623, then players will go behind some playfields and in front of others. In our sample program players will go behind playfield objects drawn with color 1 or 2, but in front of objects drawn with color 3.
The best way to learn to use the priority register is to experiment with it.* Let's modify our program to do that by making these seven changes:
*I am deliberately avoiding, here, the usual discussion of "playfields 0 thru 4." That's because it's difficult to define how a specific playfield is created using SETCOLOR, COLOR, PLOT, and DRAWTO statements.
1. Change line 1 so that it reads:
GOSUB 4000:GOSUB 2000:GOTO 200
2. Add this subroutine starting at line 4000:
4000 ? "What number would you like to poke into the priority register?":INPUT PR
3. At line 10050 set the variable PRIOR to 623.
4. Delete the RETURN statement at line 11095.
5. Add line 11110 as follows:
6. Put a RETURN at line 11299.
7. Change line 2 to:
After making these changes save the program and then run it. Try poking different numbers (1,2,4,8) into the priority register. Then move your player in front of the different colored playfield objects on the screen. Make a note of the results.
If you're going to take a break, now is a good time. When you come back, we'll discuss speed.
In some situations you may want to change the speed of a player. For example, in a horse race simulation, you might want to assign different speeds to the different horses. In a baseball game, you might want to assign various running speeds to different players. To do this all you need to do is set SP to different values.
Experiment with Different Speeds
To experiment with different speeds, try adding line 4010 as follows:
?"ENTER A NUMBER BETWEEN 1.0 AND 2.0 TO SET SPEED OF PLAYER.":INPUT SP:RETURN
Also, delete SP=1 from line 10050. Then run the program several times, setting SP to various numbers from 1.0 to 2.0. When you've got the player's speed under your control, continue reading.
Now try to set the player's speed even faster--to a number greater than 2.0.
- What happens when you move the player vertically? Horizontally?
*I suspect that when Atari updates its PMG system, a vertical position register will be added. After all, the Commodore 64 has one! In the meantime, we have to manage with other means to move our players vertically.
To move the player vertically, we have to write the player image data into different bytes of the player image string. Consider for a moment the data that we put into IMAGE1$:
- Notice that the first two bytes of IMAGE1$ contain zeros. What do the last two bytes contain?
These zeros serve the purpose of automatically erasing the player image as we move it around within PLAYER0$. Let's look at some specific examples. Suppose we position the player near the bottom of the screen at vertical position 80. To do this we might use these commands:
The diagram below shows what will be in each byte of PLAYER0$:
(and so on)
PLAYER IMAGE DATA
Notice that our player image data is located at bytes 80 thru 94. Now suppose we want to move our player upward by one byte. To do this we need to move all 15 bytes upward. For example, the value 76 in byte 90 will be moved up so that byte 89 will contain 76.
- Consider byte 92. Before we move the player, byte 92 contains 68. This is the data for the bottom of the player (his feet). When we move the player image data up one byte, what will be in byte 92?
Since we have two zeros at the beginning of IMAGE1$ and two at the end, we can move the data in one or two byte increments and the "trailing zeros" will automatically erase the player image.
- We can still move the player vertically in three byte increments. But we will need to change IMAGE1$. What changes would we need to make?
To make this change, we would need to change line 11300, so the data matches that given above. We would also need to change line 11050 so that it reads:
11050 DIM IMAGE$(17)
And line 11060 should be:
11060 FOR 1=1 TO 17:READ A:IMAGE1$(I,I)=CHR$(A):NEXT I
Make those changes. Then try setting SP to 3. Your player will now erase himself completely as he moves vertically at a speed of 3 bytes per move!
LOOKING AT PM DATA
To get a better idea of how PM data is stored in PLAYER0$, try this. Move the player to some vertical position of interest. Then press the SYSTEM RESET key. Next type in and run the following routine. You can run the routine simply by typing "GOTO 600," and pressing RETURN. The routine will show you the contents of PLAYER0$ byte by byte. To temporarily halt the routine, hold down the CONTROL key and press "1." Do the same to restart it.
600 GR.0:TRAP 610:FOR I=1 to 128:? I;" ";ASC(PLAYER0$(I)):NEXT I:STOP
Well, we've come a long way. But there is still a lot to learn about PMG! For one thing, although our player moves around the screen at our command, she looks funny because her legs don't move! In the next chapter you'll learn how to fix that.
For your convenience at the end of this chapter is a complete listing of the program. It's set up so you can choose the value to poke into the priority register. You can also choose the value for SP, which determines the speed of the player.
Return to Table of Contents | Previous Chapter | Next Chapter