Handling sprites collisions and scrolling from BASIC: coding a simple “game” (C64)

This article is intended as a companion to my previous article “Programming sprites on the Commodore 64, a simple tutorial using BASIC V2”. In facts, most of the concepts you can find on that post will be used here. In addition to sprite programming, we will also talk about simple tasks like updating score and time, scrolling a very minimalist background both upwards and downwards, and generating random numbers. All of this using BASIC V2 with no machine language aid.

The program that will be discussed here is a simple game called “Climber”, clearly inspired to the Atari 2600 game Crazy Climber, but featuring a much simpler gameplay. Talking about graphics, I merely copied the main sprite from that game, since I am more or less useless at sprite drawing (at least, for the moment). I also needed to draw a star, but I don’t know if I have been successful enough on that. Well, to make things clear, the two little yellow objects that you will see on the screen are meant to be stars…

 

Climber, my pseudo-semi-decent BASIC C64 game.

 

Crazy Climber, the original game (Atari 2600 version).

 

Since I needed a very simple game concept (I am not on game programming currently, and I had to cope with the limits of BASIC as well), I just decided to keep things rather simple. The player is supposed to reach as many random positions as possible before time expires. In particular, the main character is climbing a building, and he must try to reach any window that turns red. Why on earth do windows turn red? Is some magician around? Well, let’s say you may choose the option you prefer. In other words, there’s no story behind this game. Given the original Atari 2600 game, I just coded what BASIC V2 and especially my skills made possible.

I have done this program because I wanted to use my BASIC V2 scrolling routines for something a bit more interesting than just a scrolling chessboard. So, I ended up with this weird, strange, [censored] “game”. But don’t take this software as a Commodore 64 game. It’s just a  programming example. And despite its simplicity, I did have to come up with a lot of tricks to make it work smoothly enough. It’s not perfectly flicker-free, but for BASIC V2, I think it runs quite fast. So, I am reasonably sure that  it contains many hints for beginners wanting to code their own C64 BASIC V2 game (hopefully, starting from better ideas than mine).

 

DOWNLOAD: CLIMBER (Commodore 64 BASIC V2)

 

NOTE: it’s better to use this program on a stock Commodore 64 computer with no cartridge inserted, as some cartridges may affect timing.

You control the climber with the keyboard. Press U to go up, J to go down, A to go left and S to go right. You must reach the red windows the faster that you can. Each time you touch a red window, it turns back to its original colour and you get 100 points. Furthermore, a couple of stars will follow you during your path. You can use them to displace a red window if you think it is too far to reach. Just touch a window with a star to displace it. Displacement is random, but it may help you to reach the window faster. Please note that if a red window goes near the upper or lower borders, it will disappear. So you’ll have to pay attention if you want to catch a red window with a star.

The game is all a matter of speed and luck. The red window will change position on a regular basis.

You don’t have to reach the top of the building, you just have to touch as many red windows as you can on the given time limit. As your hi-score will be stored, you may try to beat yourself by scoring higher and higher.

 

 

Is all of that FUN? Honestly, I am not quite sure. But let’s talk about some programming details.

 

Scrolling technique

 

For those familiar with this blog, that’s nothing new. It is the same scrolling routine used for my scrolling chessboard programs. Scrolling is performed by just changing a couple of bytes in memory on each frame, altering the shape of programmable characters already on screen (please refer to this article if you haven’t already read it).

The red window is done with a sprite. This is the simplest and fastest way to do it. A sprite can be moved quite fast, and it is also very easy to hide (if V is the base address of the VIC-II chip, register V+21 can be used to turn off a sprite with just one POKE). So, if we need to draw a red window, we don’t have to POKE characters codes on RAM and color codes on colour RAM.  That would be too slow. And scrolling would be even slower.

On lines 503 and 522, scrolling of the whole background (including the red window), is accomplished by using just three POKEs. The vertical position of sprite 3 (red window) is updated according to the vertical position of the skyscraper. The counter K is used for both programmable characters and that sprite.

 

Making the scrolling routine interactive

 

The scrolling routine by itself (seen as a demo effect) is not that difficult. But using it on a game requires to add many things.

Although we are only changing the shape of a couple of programmable characters, we can just think that we are doing a scroll the usual way. So, we can imagine we are adding a new row of characters each time an 8 pixels scroll is performed. We just have to keep track of odd and even rows. That helps us understand where we are and what we have to do when moving up and when moving down.

The variable OD is used for that. Its sign tells us if we are on an odd or even row. Each time we start a scroll, the sign of OD is reverted. Why odd and even rows? Well, we have rows with windows and rows without  them. We just have to recognize what kind of row we are dealing with.

Let’s have a look at the UP scroll routine.

500 od=-od:k=0:ifdw=1thentp=e:e=f:f=tp
502 poke 2040,130
503 waitr,s:pokec+k,f:poked+k,e:pokev+7,wy+k:k=k+1:te=te+1:ifk=4thenpoke2040,131

504 ifk<5thenpokekb,0
505 ifk<8then503
506 ifsf=0thenwy=wy+8
507 ifsf=1thenpokev+7,100
510 tp=e:e=f:f=tp:dw=0:ifpeek(v+7)<=234thenpoke2040,129:tm=tm+j2:return
512 poke2040,129:tm=tm+j2:gosub660:cs=pe
ek(v+30):ifcs=12orcs=10thengosub600
513 return

 

We also have a variable DW on line 500. This is used to check whether the previous scroll has been performed upwards or downwards. If it has been performed downwards, now we are changing direction (this is the UP scrolling routine). So, we just need to swap the byte values used to update the programmable characters before performing the scroll. Otherwise, the screen would not be updated properly.

There are many other things inside this subroutine, but we will talk about them later. Just notice than on line 510, the values of the bytes of the programmable characters are swapped. This prepares for the next upward scroll. If the next scroll is downwards instead, another swap is performed before scrolling (see line 520 in the program).

 

Updating time

 

Since the game has a time limit, we need an exact time updating method. In machine language, we would just use interrupts. But dealing with pure BASIC, we need some sort of multitasking. We could take advantage of the system variables TI and TI$ for time, but I just wanted to handle it all by myself.

A variable J is used to hold time. It starts from 100 (we have 100 pseudo-seconds available at the beginning).

Keeping a constant time update on this program is not quite straightforward. We have to update time from the main loop. But, when a scrolling subroutine is called, we must keep updating time. Otherwise, we would have an incorrect time measure.

So, a variable TM is used. While we are on the main loop, TM is incremented by one on each iteration. When it reaches or exceeds the value JF, the time J is decremented by 1. Please have a look at line 100. At that point, the subroutine from line 1850 is called to update the time on screen. Then, the value of J is checked. If it is 0, we have game over.

On each scrolling routine, the variable TM is added the quantity J2 at the end of each scroll. Since we cannot update time during each 8 pixels scroll (we would loose speed), we just update TM at the end of the scroll, by incrementing it by a value bigger than one. This way, we recover the missing update of TM while scrolling.

When moving left or right, we may be missing a good time update as well. To recover from that, TM is incremented by 2 each time we move left or right.

Please note that on the subroutine TM is not checked, it is only checked on the main loop. But it just grows at a different rate depending on which part of the program is being executed.

With all of these adjustments, the timer keeps being updated at the same rate despite of what happens on screen. Each pseudo-second of the timer is roughly 1.6 real seconds.

Another time variable is TE. This is not showed on screen, but this is used internally to decide whether it’s time or not to change the position of the red window. It is not only updated on the main loop, but also each time a scrolling routine is called and each time the climber is moving left or right. This is done, again, to preserve an exact timing.

I don’t think I have coded a quartz clock but still, the timer seems to be exact enough.

 

Random numbers and random windows

A window becomes red thanks to the following subroutine:

600 te=0:sf=0:ifod>0thendy=0:goto 603
601 ifod<0anddw=0thendy=8
602 ifod<0anddw=1thendy=-8
603 wp=((int(rnd(1)*8))+1)*16+88+dy
604 px=(int(rnd(1)*6)):cx=0:ifpx>2thencx
=8
606 wx=px*24+104+cx:waitr,s:pokev+7,wp:p
okev+6,wx:pokev+21,15:wy=wp+1:return

 

Random numbers are generated by using the function RND. An expression such as:

N = INT(RND(1)*6)

will create a random integer number from 0 to 5.

The expression on line 603 determines the Y coordinate of a red window. Notice that we want red windows to appear from the second upper row to the penultimate lower row. So, we need a random number ranging from 1 to 8. We just need the expression for a number from 0 to 7, then we add a 1 to the result. Since we have a window every two rows (in the Y direction), we have to multiply the random number by an offset of 16 (each row is 8 pixels in height). Then, we add 88 which is the minimum value of the Y coordinate of a window. Finally, we add the correction amount DY. This is calculated on lines 600, 601 and 602 taking into account which row we are in (even or odd), and whether the DW flag is 0 or 1 (that is, whether the last scroll performed was upwards or downwards).

The X coordinate of the red window (WX) is calculated in a similar way. We now need a random number from 0 to 5, as we have 6 windows (this is generated in line 604). Notice the correction factor CX. It is needed, as the central part of the building doesn’t have windows. CX is used on line 606, where WX is calculated.

 

What happens when a window disappears while scrolling

The code is set up so that when a red window reaches the upper or lower row of windows, it disappears (its Y coordinate is checked  on lines 510 and 530). When the conditions on lines 510 and 530 are not true, the subroutine at line 660 is called.

660 te=55:sf=1:pokev+21,7:return

This short subroutine turns off the red window (by turning off the matching bit of sprite 3, leaving the other sprites on). It also sets the timer used for windows to a value near the limit. This will make another red window appear in a couple of seconds (line 90 will call the red windows subroutine at line 600).

Please note that the flag SF is set to 1. This is used in lines 507 and 525. When this flag is set to 1, the Y coordinate of a red window is forced to 100. That makes the conditions in either line 510 and 530 true. So, we simply return from the scrolling routines.

 

A star is catching a red window

To check if a star is touching a red window, me must use the sprite collision register (V+30). The decimal value for bit 3 is 8 (sprite 3 is the red window). Stars are sprites 1 and 2, so their decimal values are 2 and 4. So, we have a star/red window collision if the content of V+30 is either 10 (2+8) or 12 (4+8).

This check is performed in lines 512 and 532. The subroutine at line 600 is called to generate a new red window.

 

The climber is touching a red window

This check is performed by line 99:

99 cs=peek(v+30):cf=cf-1:ifcf<0thenifcs=9then gosub1900

As the climber is sprite 0 (decimal value of bit 0 = 1), and the red window is sprite 3 (decimal value of bit 3 = 8), the collision register V+30 is checked for a value of 8+1 = 9. Please note that the collision is only checked when the counter cf is less than 0. This is required to avoid that the same collision is detected twice. Since BASIC is slow, it may happen.

When a collision between the climber and a red window happens, the subroutine at line 1900 is called. It does the following things:

  • it turns off sprite 3 (the red windows being hit disappears);
  • timer TE is reset;
  • the colour of all windows is temporary changed, to create a flickering effect;
  • the score is updated (touching a red window is worth 100 points);
  • the counter CF is reset to 3.

Now, if the VIC-II register V+30 reports the same collision (due to the slowness of BASIC), it won’t be noticed as CF will be still positive. By the time CF becomes negative, this fake collision will be no longer detected.

 

Updating information on the screen

To show information like time, score and hi-score on the screen, simple BASIC subroutines are used. Those are based on printing control characters, so that only a certain part of the text screen is updated. On each subroutine, a home character is printed, then the cursor is moved properly before printing information.

For instance, the subroutine at line 1850 is used to update time on screen. Basically, it prints a home character, than moves the cursor, prints a few spaces to delete the old time, then goes back with the cursor and prints the updated time information.

Please note the use of the instruction WAIT. This is required to avoid flickering while updating that information on screen. This is just one of the techniques discussed on this article.

 

Some final notes

There are other aspects that may be worth discussing. For instance, the way the keyboard is read. The keyboard buffer is set to empty at times (POKEing a 0 into location 198 decimal). This is to avoid too much inertia of the controls. This is done, for instance, in line 50.

While scrolling, keystrokes are allowed to go into the buffer for a short time. This is to allow for a little bit of inertia. Otherwise, the player would have the feeling that U or J keys were not responding at times. Just have a look at lines 504 and 523.

One more note. The flashing characters at the beginning are simply obtained by changing the background color register (53281 decimal). Those characters are reversed, of course. BASIC would be too slow to change several colour memory locations, so that’s the only way to obtain such an effect in pure BASIC.

I wanted this game to be quite in the old style. So, I have used DATA statements for sprite data, like programs on manuals and magazines. This is not efficient, as it wastes memory. And some time is also required for data loading on memory. But, this way it is quite easy to change sprite data if you want to (just replace the DATA lines with your sprite DATA, CBM prg Studio will be of great help).

The program must also copy some ROM characters in RAM, so that messages can be written on screen. That’s why you have to wait some seconds before the program starts.

Previous Entries Game review: Hunchback (Commodore 64) Next Entries Sharing data between PC and C64. My PC setup using OpenCBM

Leave a Reply

*