SPRITES, OBJECTS AND PROGRAMMING YOUR OWN SIMPLE GAMES

Introduction / Example Sprite Program / Game 1 - Moon Madness - Episode 2 / Allowing Sprites to Move MORE than 256 pixels / Game 2 - Universal Blast Duel / Game 3 Missile Blasta / Game 4 - For Speed We Need / Hardware Sprite/Background Collision + Game 5 Give the Dog A Bone / Software Related Sprite to Background Collision / Game 6 - Hyperblitz
/ Object Spawning / Game 7 - Rogue Ninja
/ Sprite Properties with Macros / GAME 3B: Missile Blasta Remastered V2
INTRODUCTION TO SPRITES

What is a Sprite?

A sprite is a small object that can be moved around the C64 screen. A sprite can be often used in games and demos. They are often used as actors or objects inside a game production. Sprites can be all shapes and sizes, and they can be repositioned, moved and / or also use hi-res or multicolour. They can also be overlayed. Later on we are going to try and make a few games. A later chapter will be showing you advanced game techniques. First of all, a sprite looks something like this:

Hi-res sprite:

Multicolour sprite:

If two sprites are combined together overlapping each other. It is also known as a sprite overlay.

Turning On/Off Sprites

We use $D015 to turn on sprites. To turn a sprite on we can use LDA #$01, STA $D015, or if you wanted all 8 sprites turned on, we use LDA #$FF, STA $D015. This will be easy to remember. Binary is also helpful to switching on/off sprites.

EXAMPLE:

LDA #%00000001    ;Enable sprite 0, and disable sprites 1 to 7
STA $D015

LDA #%10101010    ;Enable sprites 1,3,5 and 7, and disable sprites 0,2,4 and 6
STA $D015

LDA #%11111111    ;Enable ALL of the sprites
STA $D015

It is also possible to use decimal/hexadecimal codes to switch on/off sprites. The best way to find out those values would be to use a BINARY/DECIMAL/HEX calculator program. There maybe one or two on the CSDB web site.

Setting Sprite Properties (Sprite Type)

Also, setting up your sprite correctly would be quite tricky. Anyway, because we are using bank $03 on the C64, we will use $07F8 - $07FF for our sprites. However, because our sprite data is loaded at $2000, we will need to use LDA #$80, STA $07F8 (for sprite 1), STA $07F9 (for sprite 2),etc. LDA #$80 reads from the first few lines at $2000 and then pastes it into $07F8, etc to perform a perfect display for your sprites.

EXAMPLE: SPRITE 0 TYPE, BANK #$03, FRAME AT $2000, SCREEN AT $0400-$07E8

LDA #$80
STA $07F8

EXAMPLE 2: SPRITE 0 TYPE, BANK #$02, FRAME AT $6000, SCREEN AT $5C00-$57E8

LDA #$80
STA $5CF8

EXAMPLE 3: SPRITE 4 TYPE, BANK #$03, FRAME AT $3000, SCREEN AT $0400,$07E8

LDA #$88
STA $07F8

You will get to know this, when you go on to the first example game, which follows this part of the chapter.

Sprite Positioning

To position sprites, we use $D000 - $D00F. Why are there 16 instead of 8 values? Well, the reason for this is because $D000, $D002, $D004, $D006, $D008, $D00A, $D00C, $D00E use the sprite's x-axis, while $D001, $D003, $D005, $D007, $D009, $D00B, $D00D, $D00F all use sprite's y-axis, both of these are according to the sprite number. Here is a simple routine to get you started on how to position, display and turn on sprites. Call out a start (*=xxxx) and SEI then enter the following.

There are 2 different ways how to position sprites, basically you can use the HARDWARE values, or use ghost bytes then store those to HARDWARE values. If you don't need to expand sprite positioning, then hardware is simple enough to position sprites. (Take a look at the table from earlier for more info about the hardware pointers for Sprite position X, Sprite position Y for each sprite (Sprites 0 - 7). 

HARDWARE EXAMPLE (Positioning a sprite at #$70, and #$89)

LDA #$70
STA $D000
LDA #$89
STA $D001
RTS

SOFTWARE EXAMPLE (Positioning a sprite at #$70 and #$89)


LDA #$70

STA SPRITEX
LDA #$89
STA SPRITEY

;Selfmod sprite positioning table
SPRITEY=SPRITEX+1 ;SPRITEY is the next byte after SPRITEX
SPRITEX !BYTE $00,$00,$00,$00,$00,$00,$00,$00
        !BYTE $00,$00,$00,$00,$00,$00,$00,$00

EXPANDED POSITION USING THE SOFTWARE EXAMPLE (Allow sprites use full screen)

Called by an infinite loop inside a game loop or IRQ raster interrupt. This subroutine reads the ghost bytes/or self-mod bytes of the sprite position values and stores the X,Y position to the hardware sprite position. 

FULLMOVE:
    LDX #$00
XLOOP
    LDA SPRITEY,X
    STA $D001,X
    LDA SPRITEX,X
    ASL                     ;Double the X-screen position size for sprites

    ROL $D010 
    STA $D000,X
    INX ;Read the next sprite's
    INX ;X,Y position
    CPX #16 ;or #$10 - Max of 16 pointers to use ($D000-$D00F for sprites)
    BNE XLOOP
    RTS

SPRITEY=SPRITEX+1
SPRITEX !BYTE $00,$00,$00,$00,$00,$00,$00,$00
        !BYTE $00,$00,$00,$00,$00,$00,$00,$00 

Moving Sprites

Moving sprites are usually done by incrementing/decrementing the position of each sprite.X or Y position. The best way to calculate a movement of a position for a sprite would be to one of two methods:

MOVING SPRITE 0 X POSITION WITH ONLY HARDWARE

LDA $D000
CLC
ADC #$01 ;Number of pixels to move an object
STA $D000

or

LDA $D000
SEC
SBC #$FF
STA $D000

or

INC $D000

MOVING SPRITE 2 Y POSITION USING SELF MOD TABLE (From one of the above examples)

LDA SPRITEX+3
CLC
ADC #$01
STA SPRITEX+3

or

LDA SPRITEX+3
SEC
SBC #$FF
STA SPRITEX+3

or

INC SPRITEX+3

Sprite Colours

This is something which we have not looked at in this feature. Sprite's colours are simple. We have a hi-res sprite which needs a touch of colour, so here's how it is done. The colour refers from $D027 (sprite 1) to $D02E (sprite 8)

LDA #$colour
STA $D027

You can even toggle multicolour sprites, using LDA #$FF, STA $D01C (Multi colour) and to change the 2 multi-colours, we use STA $D025 and STA $D026. $D025 uses multicolour 1, and $D026 uses multicolour 2. To get these to work, we need to set LDA #$colour before STA. The 'colour' has to be between #$00 - #$0F, as these are the main 16 colours. (Please refer to your C64 user guide).

Priorities

Sprites have their own priorities. You can put certain sprites in front or behind the characters on screen. This can be toggled by using $D01B. For example LDA #$00, STA $D01B puts all sprites over the text, and LDA #$FF, STA $D01B puts all sprites under the text. The thing is that you can actually toggle the sprites moving over and under the screen (like in a classic intro screen, which involves a green sprite bar going under and over the logo), however, advanced techniques would be required. We'll be taking a look at this later on in the feature.

EXAMPLE: Sprite 0 behind background, using binary

LDA #%00000001   
STA $D01B

EXAMPLE: Sprite 0,2,4,6  behind background, using binary. Other sprites in front


LDA #%01010101   
STA $D01B

Sprites can also use MSB $D010 to expand the position, so that the sprites can go across a full screen rather than miss the last part of the screen. The game code example will show you how this works.

Expanding Sprites

Another priority, which sprites have is expanding in two different ways x, and y axis. This transformation can sometimes be a good laugh to try (like I did in a BASIC demo called Biblet Land in 1996), but how do we expand our sprites? We use LDA #$FF (for all sprites), STA $D017, and STA $D01D. $D017 expands 'x' and $D01D expands 'y' for the sprites. You can turn one expansion off and another on.

EXAMPLE: Sprite 1 Expanded X,Y

LDA #%00000010   
STA $D017
STA $D01D

EXAMPLE: Sprite 2 Mixed expansion

LDA #%00000100
STA $D017

LDA #%00000000
STA $D01D

Hardware Collisions

Sprite/Sprite hardware collision is used with $D01E. But we will be using SOFTWARE sprite/sprite collision
Sprite/Background collision is used with $D01F. Collision detection is slightly different. You need to check a value for an object to have hit another object. You also need to compare a value of whether or not a sprite has crashed into another sprite. For example. Sprite 0 hitting any other sprite:

LDA $D01E
CMP #%0000001 ;Any sprite hitting the player.
BNE NOCOLLISION
JMP PLAYERDEAD
NOCOLLISION
RTS

Software collision

Sprite/Sprite collision is used by creating and storing values according to the virtual sprite position co-ordinates. The collision subroutine checks whether or not one sprite is in range of another sprite. It uses a one size fits all approach.An Example of this is set out on GAME 1 and 2's example. Basically something like:

LDA $D01E
CMP #$01 ;Other Sprites collided into SPRITE 0
BCS NOTHIT
INC $D020
NOTHIT
RTS

Hardware Sprite/Background Collision

This is a very simple approach to detecting a sprite hits a background, using hardware $D01F. This is used exactly the same way as the player colliding into a sprite. Give a Dog a Bone will show you this.

LDA $D01F
CMP #$01 ;Other Sprites collided into SPRITE 0
BCS NOTHIT
INC $D020
NOTHIT
RTS

Software Sprite/Background Collision

Software Sprite/Background collision is more complex where checks through a table for a killer char on screen, and then processes a collision. Most games use the software collision, than the hardware collision, as it is more reliable and handles collisions well - depending on the program. We will be showing you this in the 2 player game 'Universal Blaster'.

BACK TO TOP

PROGRAMMING YOUR FIRST SPRITE PROGRAM

Just for fun, we are going to program our first sprite program. I should warn you, it is NOT going to be all that exciting, but it will give you a basic idea of how easy it is to display and move sprites around the screen. Simply draw 1 single sprite in C64Studio's sprite editor (or use Charpad) then EXPORT your single sprite as a frame and then IMPORT it into the source code.  

;Sprite fun by Richard Bayliss
;An introduction to drawing and moving
;sprites (Using non-expanded MSB mode)

                !TO "SPRITEFUN.PRG",CBM
               
                *=$0801
                !BASIC 2018,2064 ;Generate SYS 2064 call
                *=$0810
                SEI
                JSR $E544
               
                LDA #$0C    ;Grey border
                STA $D020
                LDA #$0B  ;Grey background
                STA $D021
               
                LDA #%11111111   ;Enable all sprites (You can use #$FF or 255 if your wish)
                STA $D015
                LDA #%00000000   ;Disable other sprite properties which are:
                STA $D017              ;Sprite Y-EXPANSION
                STA $D01B        ;Sprite behind background
                STA $D01D        ;Sprite X-EXPANDSION
               
                ;Draw all sprites as same sprite from $2000
               
                LDX #$00
DRAWSPRITES
                LDA #$80        ;Frame to read
                STA $07F8,X ;SPRITE0-7 TYPE
                LDA #$01
                STA $D027,X ;SPRITE0-7 COLOUR
                INX
                CPX #$08
                BNE DRAWSPRITES
               
;Now sprites have been drawn. Set a default position where all sprites
;a placed at the centre of the screen.

                LDX #$00
POSITIONSPRITES
                LDA #$78 ;SELECTED X POSITION
                STA $D000,X ;SPRITE0-SPRITE7 X POSITION SET
                LDA #$88 ;SELECTED Y POSITION
                STA $D001,X
                INX             ;ADD 2 TO READ TO THE NEXT POSITION
                INX             
                CPX #16  ;ALL 16 POSITIONS READ?
                BNE POSITIONSPRITES ;MAYBE NOT
               
;All sprites are positioned, let's make a simple raster delay
;then allow sprites to move according to a sprite position read table

LOOP
                LDA #$FA ;RASTERLINE
                CMP $D012
                BNE LOOP
               
                ;Move all sprites via a loop
               
                LDX #$00
MOVESPRITES
                LDA $D000,X
                CLC
                ADC MOVETABLE,X
                STA $D000,X
                INX                       
                CPX #$10
                BNE MOVESPRITES
                JMP LOOP
               
;Sprite movement / speed table

MOVETABLE
                !BYTE $00,$FF ;SPRITE 0 X/Y SPEED - NORTH
                !BYTE $01,$FF ;SPRITE 1 X/Y SPEED - NORTH EAST
                !BYTE $01,$00 ;SPRITE 2 X/Y SPEED - EAST
                !BYTE $01,$01 ;SPRITE 3 X/Y SPEED - SOUTH EAST
                !BYTE $00,$01 ;SPRITE 4 X/Y SPEED - SOUTH
                !BYTE $FF,$01 ;SPRITE 5 X/Y SPEED - SOUTH WEST
                !BYTE $FF,$00 ;SPRITE 6 X/Y SPEED - WEST
                !BYTE $FF,$FF ;SPRITE 7 X/Y SPEED - NORTH WEST

       
;Import binary (SPRITE DATA)
    *=$2000               
    !BINARY "MYSPRITE.SPR"
               
RESULT:

This was quite a basic example on sprites being moved inside a loop. So how were the sprites being able to move? A loop controlled inside a raster, calls a routine to pick up a current position of a sprite. It then reads a positive or negative value from the sprite movements table (MOVETABLE) and then updates the position of a sprite by adding or subtracting by the value of the sprite table. 

CHALLENGE 1:

By playing around with the tables. Reverse the process, but double the speed of the sprite movement. Also enable expanded sprites.
        

BACK TO TOP


GAME 1: Moon Madness - Episode 2 - Infinite destruction

Back in the year 2000, I wrote a simple, but rather buggy game called Moon Madness. Now 18 years later a sequel features in this chapter, we are going to replicate the naff game, but make it much better. First of all, you will need to draw your own title and game screen (Or if you want to, you can just download the data+source that has been implemented into this example, simply by clicking on the image). This example game will be using no expanded sprite MSB positioning and the collision detection is just going to be plain hardware, using D01E - allowing ALL sprites to collide into the player. 

- Player ship is located at the bottom of the screen and will only be allowed to move left / right
- A series of planets will be screaming down the screen.
- A score will be achieved for every moon passed off screen
- A hardware collision detection ($D01E) will be used for every time a moon which hits the player
- A shield counter - to indicate the damage status of the player
- Animated sprites for player + explosion.

Setting up the game:

First of all, only 1 tune is being used for this game, so I initialised the music player. It is of course a tune I wrote exclusively for this game. Addresses are set for displaying the sprites (Sprite data at $2000-$2800, Charset data at $2800,$3000, Game Screen data at $3000-$3400, Colour Attributes at $3400 - $3500, and Game Title Screen/Matrix at $3500-$3900. 

The next step was to create the title screen, which was able to mask the current score into the screen position which was intended to be read for the scoring and hi-score. The fire button is initialised. An IRQ interrupt is called, playing the music. After clearing the IRQ flag. A simple loop waiting for the player to press fire to start the game is in place. Following that. The game code commences.

The main game screen gets drawn, the sprites are initialised and repositioned. A GAMELOOP is called with a few subroutines linked to the game loop. Which are as follows:

- SYNCTIMER:  Synchronise the timer linked to the IRQ interrupt, in order to allow movement/delay of the code run more smoothly. It's actually better than linking subroutines inside the actual interrupt anyhow :)

- ANIMPLAYER: Animate the player, according to the delay and amount of frames to be read. The animation subroutine reads all of the valid bytes from a table, until it reaches the end of the table. A pointer is then reset to make the sprite animation restart from the very first byte on table. 

- PLAYERCONTROL: Tries to read the joystick in port 2, and controls the player. If LEFT/RIGHT are pushed on the joystick. The player's ship will update it's current position on to the next position at 3x the speed set in the MOVELEFT / MOVERIGHT subroutine.

- MOVEMOONS: This subroutine picks up the current position of a moon through a loop, and updates it to the next Y position, according to the value of the LEVEL zeropage. This controls the speed of the game. The moons will wrap down the screen consistantly.

-SCORING: This checks whether one of the 8 moons (which is checked through a loop) have reached a certain position #$F0 (250). If it has reached that position and is leaving the screen. Another subroutine is called to score points, via DOSCORE. The subroutine DOSCORE reads all 6 digits of the score, and then does some score adding according to the position of the SCORE table.

- MASKSCORE: Copies all 6 digits from the SCORE table, and HISCORE table and positions these on to the screen position, which was planned for the score/hi score digits.

-LEVELTIME: Game play time, before setting up the next level. This subroutine Does two tests. First it checks whether or not LEVELTIME1 has reached its maximum time limit. If it has, it updates LEVELTIME2 to go up one byte. After time has elapsed. It adds a value to the zeropage LEVEL, which speeds the game up. However after the next check If the LEVEL counter = 8, and the time is up on that level. The game will jump straight on to the ENDSEQUENCE. Which is another loop, that animates the player flying off the screen, displays the END SCREEN text, and finally updates the score + hiscore and waits for Fire to be pressed to restart the program.

-TESTCOLLISION: Tests the player's sprite/sprite collision with the rest of the sprites. If a collision value has been detected (Value above 1), a collision from all of the other sprites have been found. A subroutine is called to flash the player ship (incrementing the sprite 0 colour) and also drain the player's shield. When the player's shield is drained a subroutine is called to update the digit values of the two zeropages SHIELDDIGIT1 and SHIELDDIGIT2. (In the game loop, the SHIELDDIGIT1 and SHIELDDIGIT2 are stored to the screen position of the 2 digits to be used). The collision subroutine also checks whether or not the player's shield counter has reached 00. If it has, another loop is called which will destroy the player ship, using another loop for explosion animation. Then the GAMEOVER prompt and hi-score check is performed. The game then refreshes the FIREBUTTON zeropage and waits for the press of the fire button before restarting the program again.

Here's how this game looks. As I have said before you can download the image for the complete project to run on C64Studio:
However, if you want to see just the source code. Since code can be quite lengthy at times. I have decided to just provide links for you to view the code. If you want to play the game (Or play around with the binaries and source for this game). You click on the image below to download it all :)



VIEW SOURCE
      
BACK TO TOP

Allowing Sprites to move MORE than 256 pixels

You may have spotted that the previous 2 sections in this tutorial only limited the number of pixels for a sprite to move as 256. There are many C64 games, which allowed sprites to move further. How was this done? Basically a hardware pointer of $D010 does this trick. $D010 can be used in different ways.

Example 1 - Checking the sprite range:

    LDA $D000
    CLC
    ADC #$01
    CMP #$FF ;256TH byte reached
    BCC NOTREACHED
    LDA #$00
    STA $D000
    LDA #$10
    STA $D010
NOTREACHED
    STA $D000
    RTS

Checking for a X position of a sprite in order to expand the position, allowing full screen can be hard work. The most simplest method, which is my favourite method, is EXAMPLE 2 for just a single sprite and EXAMPLE 3 for additional sprites.

Example 2 - The sprite pointers to hardware method for a single sprite

EXPANDMSB
    LDA VSPRY    ;Grab Y position of sprite pointer
    STA $D001    ;Store it directionally on to the Hardware Sprite Y
    LDA VSPRX    ;Grab X position of sprite pointer
    ASL          ;8 bit multiplication
    ROL $D010    ;for screen expansion / sprite position
    STA $D000    ;Store it directionally on to the Hardware Sprite X
    RTS

Example 3 - Using MORE sprites to hardware sprite position method (USE JSR EXPANDMSB inside your program loop in an IRQ or Rasterline):

EXPANDMSB

     LDX #$00            ;Start a loop with X=0
LOOP
    LDA VSPRTABLE+1,X   ;Grab Y position of sprite pointer from table
    STA $D001,X         ;Store it to hardware Y sprite position
    LDA VSPRTABLE,X     ;Grab X position of sprite pointer from table
    ASL                 ;Multiply the no/pixels
    ROR $D010           ;To generate the size of the screen the sprite can move
    STA $D000,X         ;Store to X hardware sprite position
    INX                 ;Shift code to read 1 table up 1 byte
    INX                 ;Shift code to read 1 table down 1 byte
    CPX #16
    BNE EXPANDMSB

    RTS

You have already seen some of these example code snippets, but wouldn't it be better if it was used using the similar grey screen sprite demo, as I gave you earlier above CHALLENGE 1? :) Well, why not? ;) Here it comes:

MOVING SPRITES - DEMO 2 - (Using Expanding MSB, with $D010)

;Sprite fun 2 by Richard Bayliss
;An introduction to drawing and moving
;sprites (Using expanded MSB mode)

                !TO "SPRITEFUN2.PRG",CBM
               
                *=$0801
                !BASIC 2018,2064 ;Generate SYS 2064 call
                *=$0810
                SEI
                JSR $E544
               
                LDA #$0C    ;Grey border
                STA $D020
                LDA #$0B  ;Grey background
                STA $D021
               
                LDA #%11111111   ;Enable all sprites (You can use #$FF or 255 if your wish)
                STA $D015
                LDA #%00000000   ;Disable other sprite properties which are:
                STA $D017        ;Sprite Y-EXPANSION
                STA $D01B        ;Sprite behind background
                STA $D01D        ;Sprite X-EXPANDSION
               
                ;Draw all sprites as same sprite from $2000
               
                LDX #$00
DRAWSPRITES
                LDA #$80        ;Frame to read
                STA $07F8,X ;SPRITE0-7 TYPE
                LDA #$01
                STA $D027,X ;SPRITE0-7 COLOUR
                INX
                CPX #$08
                BNE DRAWSPRITES
               
;Now sprites have been drawn. Set a default position where all sprites
;a placed at the centre of the screen.

                LDX #$00
POSITIONSPRITES
                LDA #$58 ;SELECTED X POSITION
                STA VSPRPOS,X   ;SPRITE0-SPRITE7 X POSITION SET via custom
                                ;sprite positioning table (VSPRPOS)
                LDA #$88        ;SELECTED Y POSITIIN
                STA VSPRPOS+1,X
                INX             ;ADD 2 TO READ TO THE NEXT POSITION
                INX             
                CPX #16         ;ALL 16 POSITIONS READ?
                BNE POSITIONSPRITES ;MAYBE NOT
               
;All sprites are positioned, let's make a simple raster delay
;then allow sprites to move according to a sprite position read table

LOOP
                LDA #$FA ;RASTERLINE
                CMP $D012
                BNE LOOP
                JSR EXPANDMSB ;Subroutine to expand MSB sprite area
                JSR MOVESUBR  ;Subroutine to move sprites by calculating movement
                JMP LOOP
               
;Expand the position of the sprites, according to the
;position of a proposed sprite position then store
;the pointers to the hardware position

EXPANDMSB
                LDX #$00 ;Start of a loop for reading tables
EXPANDREAD
                LDA VSPRPOS+1,X ;Grab the next current table position of VSPRPOS
                STA $D001,X     ;Store it to Sprite Y Hard ware sprite
                LDA VSPRPOS,X   ;Grab the previous current table position of VSPRPOS
                ASL             ;16 bit multiplication
                ROR $D010       ;Expand the sprite position screen
                STA $D000,X     ;Store the new position to hardware Sprite X
                INX             ;read next byte of table for VSPRPOS (Y)
                INX             ;read next byte of table for VSPRPOS (X)
                CPX #16         ;16 bytes ($10) read (There are 16 pointer max for sprite positioning)
                BNE EXPANDREAD  ;If not, read the next table
                RTS             ;Finished
               
                ;Move all sprites via a loop
MOVESUBR               
                LDX #$00
MOVESPRITES
                LDA VSPRPOS,X   ;Grab virtual sprite position from table
                CLC              
                ADC MOVETABLE,X ;Add a positive/negative value according to the movetable
                STA VSPRPOS,X   ;Store the value as a speed directly to the virtual sprite posiion
                INX             ;Add a value to X loop(1 byte)
                CPX #16         ;Have all 16 bytes of the speed table been read?
                BNE MOVESPRITES ;No, read next byte from table
                JMP LOOP        ;Exit subroutine
               
;Sprite position table (VSPRPOS) - Virtual Sprite Position
VSPRPOS
                !BYTE $00,$00 ;SPRITE 0 X/Y
                !BYTE $00,$00 ;SPRITE 1 X/Y
                !BYTE $00,$00 ;SPRITE 2 X/Y
                !BYTE $00,$00 ;SPRITE 3 X/Y
                !BYTE $00,$00 ;SPRITE 4 X/Y
                !BYTE $00,$00 ;SPRITE 5 X/Y
                !BYTE $00,$00 ;SPRITE 6 X/Y
                !BYTE $00,$00 ;SPRITE 7 X/Y
               
;Sprite movement / speed table

MOVETABLE
                !BYTE $00,$FF ;SPRITE 0 X/Y SPEED - NORTH
                !BYTE $01,$FF ;SPRITE 1 X/Y SPEED - NORTH EAST
                !BYTE $01,$00 ;SPRITE 2 X/Y SPEED - EAST
                !BYTE $01,$01 ;SPRITE 3 X/Y SPEED - SOUTH EAST
                !BYTE $00,$01 ;SPRITE 4 X/Y SPEED    - SOUTH
                !BYTE $FF,$01 ;SPRITE 5 X/Y SPEED - SOUTH WEST
                !BYTE $FF,$00 ;SPRITE 6 X/Y SPEED - WEST
                !BYTE $FF,$FF ;SPRITE 7 X/Y SPEED - NORTH WEST

       
;Import binary (SPRITE DATA)
    *=$2000               
    !BINARY "MYSPRITE.SPR"

RESULT:

            
  
BACK TO TOP

GAME 2: UNIVERSAL BLAST DUEL

We are going to create a small 2 player game, where you have two ships. We'll be using three sprites for this tutorial, but in chapter 11, we will enhance the game more. Here is what to do. Using the sprite editor, draw 2 triangles (not in multi-colour) One pointing up, and the other pointing down, then draw 1 small circle. The two triangles will be the two players and the circle will be the player's bullet. Save your sprite data, and rip or compose your own demo tune using any music composer which initialize your tune at $1000 and play at $1003. Save your music to disk. However, If this is too much hassle to get you started then I have attached a .D64 image with the data and code. You'll just need Turbo Assembler. Please read the 2 note files supplied with the code and data. There is also a runnable file for you so you can see the sort of game, we're teaching you to create :). The runnable file should look something like this. Yeah, I know. I used basic shapes, but what the heck?


.... and now, the code, fully documented:

SOURCE CODE

GET FULL BINARY+PROJECT SOURCE

Quite a lot of code there eh?. Well, more to come :)

Enhancing Your Game, using Bitmap/Hires Graphics

I wont add the whole listing to the game again, but not to worry, I've added another .D64 image, which consists of the game code and extras. What I've done for the enhanced game is add a picture, converted into Vidcom Paint format, as that way it is easy to remember what banks and charset memory to use. You could save your picture using KOALAPaint, but you would need to use the Comic Pirates' Picture Splitter program, which can be downloaded from the CBM64 FTP sites. Anyway, let me tell you about Vidcom paint images shall I?

First of all, the Vidcom Paint images are compressed to 40 blocks and uses the following locations for the image. First of all, the image uses BANK #$02 so that memory from $4000 - $8000 can be read. If this is the case then a sprite has to move to a newer location. We use $5800 - $5be8, where the data for our colours to be pasted into the main color RAM (If you don't know where the colour RAM is, take a look at an earlier chapter. We use $5C00 - $5FE8 for the colour data, which is indicated by the charset memory using $D018. Finally we use $6000 - $7F3F for the bitmap, where that also uses $D018. We need to use a correct POKE for $D018 to display the screen accurately.

Right, now I have mentioned the technical part about Vidcom Paint graphic images, it is time to show you how to actually display them. First of all, load up your image, load Turbo Assembler, use G9000 in Action Replay M/C monitor or SYS36864 and enter * = (spare memory location you want to use), enter a loop which will clear the screen now enter the listing, which is as follows:

LDX #$02                 LDA #$02
LDY #$78                 STA $DD00
LDA #$3B or              LDA #$78
STX $DD00                STA $D018
STY $D018                LDA #$3B
STA $D011                STA $D011

Now that we have set $D011 in bitmap mode, if you display the Vidcom picture, it looks a sort of mess - colour wise, so now we do an additional routine, which will copy all data from $5800-$5BE8 to the screen RAM. Here's how it's done:

LDX #$00
PAINT LDA $5800,X
STA $D800,X
LDA $5900,x
STA $D900,X
LDA $5A00,X
STA $DA00,X
LDA $5AE8,X
STA $DAE8,X
INX
BNE PAINT

Now let's add HOLD JMP HOLD, assemble and then test. Viola, an accurate bitmap picture displayed at last :o).

Okay, what about our game? Well, I have attached a zipped complete C64 project image, so you can download it. It consists of all the data, for music, sprites and bitmap. The assembler and also the game code. There is also a runnable file of this game too. :o) Beware, because of the size of the code in Turbo Assembler, the bitmap will mess up. All you need to do is load your bitmap, once the assembly is complete and everything works. :o)

SOURCE CODE

FULL SOURCE+BINARY DATA

RESULT:

BACK TO TOP

GAME 3: MISSILE BLASTA - (Remastered Edition)

In this chapter, we are going to be working on a 1 player blasting game. First of all. We are going to have a player, bullet and also enemy. This game is going to feature sprite animation (unlike the previous game example). Animating sprites are not that easy, unless we created a routine, which would read from the sprite table and animate these. Here's what you need to do (or just look at the example .d64 image). We are going to be placing music at $1000, sprites at $2000, charset at $2800. Now using a screen editor or any other tool, draw a nice little space background (Blueaugh! My background screen and sprites suck big time - the data and code was very old anyhow.

So how will this game work?

First of all, the game will consist of 8 different sprites. One sprite for the player. A sprite for the player's bullet. Also 6 sprites for missiles. Each sprite (apart from the bullet) is to be animated. Also for the timebeing the bullet can explode every time it hits a missile. Once a missile has reached the very bottom of the game screen. It gets repositioned at a different horizontal position, according to a simple randomizer (table constantly pulled all the time).

The sprite/sprite collision uses a software based collision with the player. The collision is based on a 'one size fits all' where the X and Y co-ordinates of a current position of the player is read. Should any part of a sprite reach the area within the range of the player. A collision is formed, and the player will lose a shield. After the player has lost its shield. It will explode, and the game is over.

SOURCE CODE

Grab the C64STUDIO Project and Binary Data

RESULT:

CHALLENGE 2:

By looking at the game code. See if you can add some more random sprite position tables. Also add a GAME OVER and ENDING SCREEN. I deliberately left those out for you as a challenge.

BACK TO TOP

FOR SPEED WE NEED

In this chapter, we take a look at For Speed We Need V2 game. Well before I give you the code let me tell you more about this simple game. First of all, it is one of those simple dodge and avoid games,  which consist of 4 levels of different speeds. It also involves each level being timed by a clock. Plus simple sprite to sprite coliisions using $D01E (It is better to not use this function if you're doing more advanced game programming). Also this features a rough background scroller, which loops if '@' is detected in the M/C using very old code. Well, it was originally made in 2003 in Turbo Assembler. The code and program files have now been ported to C64 Studio. 

There are different parts of the code which you should be familiar with, but not everything is as familiar as it could have been for you. There are some newer routines, such as making the game more stable, instead of using raster splits that use CMP $D012. A double interrupt has been used instead. I haven't really mentioned much about double interrupts, but they are very handy - especially if you want to SAVE some raster time.

You might have also noticed that I have added a SYNC routine, which tries to synchronize the main body of the game code, instead of using JSR routines for the game inside an IRQ raster interrupt. This is mainly because the IRQ raster interrupt can slow down if too many Ioops or subroutines are placed inside an IRQ. So it is best to clear the IRQ flag and syncrhonize the timing, so that sprites and data work accurately. Sometimes the SYNC mode is a pain in the backside, but if you use it right, it should work fine. I have however, deliverately put some sprite routines inside the IRQ, as the movements did not synchronize properly while music was playing. Other routines work fine :)




The Scroller: If you take a look at (11.) properly you will notice that the game scroller is not a smooth scroll. But a rough scroll routine. Where you see the $0400+(N*n),x bit in the rough scroll. This is where 40 chars ($28 chars) is read from the bottom, and is then pulled to the upper 40 chars row. After all rows are pulled. A new row of data is visible on screen from the map buffer, and the pulling routine continues.

Like myself, you'd find this routine difficult, but later on you should be able to get the hang of it :).

O.k. now here's the code 

SOURCE CODE

And here is the full C64 Studio Binary and Source Code

GAME DATA AND CODE

CHALLENGE 3:

Gee whizz. This code is ever so old, and the game looks really ugly and horrible. Your challenge is to simply implement some new game graphics, and screen and also add a front end to the game and link it to the source. Let's see what you can do with it.


Hardware sprite to background collision + animated chars

GAME 5: GIVE THE DOG A BONE
We are going to write another little mini game. This time it is a game which uses sprite to background collision detection. Before we get started, I need to point out that there are actually two different types of sprite / background collisions. They are the hardware collision and also the software collision.

For this example, we will be using the hardware sprite to background collision, which is more simple. In the next chapter, we'll be using the software sprite/background collision, which is all to do with the char type and also collision char tables. The hardware collision uses the $D01F value, which means if a sprite hits a visible char anywhere on the screen, a collision is made. This is pretty simple to detect by using the following statement in your source code:


SPRCOL      LDA $D01F ;Read sprite/char hardware
            LSR A ;Remove A if you are NOT using Turbo Assembler
            BCC HIT
               RTS ;Player is not hit, so terminate the routine
HIT         INC $D027 ;Flash sprite colour to show collision
            RTS ;End

Pretty simple huh? Well, it is pretty boring to look at as code. You would want a practical example wouldn't you?. Well, thankfully, you can download the example and the source code below to look at it in a more practical kind of way. The program below was programmed in Turbo Assembler, and shows the whole example code for the sprite/background collision. It does not look anything too exciting. It shows a blank screen with a line of the reverse on + spacebar chars and a square sprite, which will move until it hits the visible charset.

;Assemble IT - Chapter 21, part 1
;
;Sprite to background collision
;By Richard Bayliss

;Global labels/constants

objpos   = $0370
sync     = $0380

         *= $4000
         sei

;Clear the screen

         lda #$00  ;Blacken screen
         sta $d020
         sta $d021

         ldx #$00
wipe     lda #$20
         sta $0400,x
         inx
         bne wipe

;Draw a line somewhere at the bottom
;using RVS on + SPACEBAR char.

         ldx #$00
draw     lda #$a0
         sta $06d0,x
         lda #$02
         sta $dad0,x
         inx
         cpx #$28 ;(Or use #40 instead)
         bne draw

;Fill $2000 so we can make a square as
;the test sprite.

         ldx #$00
mksquare lda #$ff
         sta $2000,x
         inx
         bne mksquare

;Turn the only sprite on

         lda #$01
         sta $d015

;Put square object into sprite memory

         lda #$80
         sta $07f8


;Now set up only one sprite and its
;default position (for expansion)

         lda #$58
         sta objpos+$00 ;Default xpos
         lda #$42
         sta objpos+$01 ;Default ypos

;Set the sprite colour to white

         lda #$01
         sta $d027

;Make our interrupt

         lda #<irq1
         ldx #>irq1
         sta $0314
         stx $0315
         lda #$00
         sta $d012
         lda #$7f
         sta $dc0d
         lda #$1b
         sta $d011
         lda #$01
         sta $d01a
         cli
mainloop lda #$00
         sta sync
         cmp sync
         beq *-3
         jsr expand ;Call expansion rt
         jsr readjoy ;Call joy read rt
         jsr bgrcol ;Call bgr.coll rt
         jmp mainloop

;Expand the sprite x position for only
;the first sprite

expand   lda objpos+$01
         sta $d001
         lda objpos+$00
         asl a
         rol $d010
         sta $d000
         rts

irq1     inc $d019
         lda #$01
         sta sync
         jmp $ea7e

;Move the square slowly around the
;screen with a joystick plugged into
;port 2

readjoy  lda $dc00
up       lsr a     ;Remove 'a' if not
         bcs down ;using Turbo Assembler
         ldx objpos+$01
         dex
         dex
         stx objpos+$01
down     lsr a
         bcs left
         ldx objpos+$01
         inx
         inx
         stx objpos+$01
left     lsr a
         bcs right
         ldx objpos+$00
         dex
         stx objpos+$00
right    lsr a
         bcs fire
         ldx objpos+$00
         inx
         stx objpos+$00
fire     rts   ;Ignore firebutton as
               ;we don't really need
               ;it.

;The hardware sprite/background
;collision routine

bgrcol   lda $d01f;Hardware detect
         lsr a    ;If sprite 1 touches
         bcs hit  ;visible char then
                  ;collision detected.
              ;else sprite stays white
         lda #$01
         sta $d027
         rts

;Sprite hits a visible char so for now
;we'll make the sprite flash.

hit      inc $d027
         rts



Okay. So now you seem to have the grasp of what is going on here, it is time make a game, but before you do, I want to show you a new trick, which will be implemented into the source code. Do you remember my games such as Balloonacy and Balloonacy 2? These games used the same type of technique as above, but you also notice how the game uses animated chars. It is tricky at first, but after a while you will get the hang of the routine. It is quite nice and handy to use.


charanim lda chrptr
         cmp #$0c     ;Our actual delay
         beq enddelay ;for the anim
         inc chrptr   ;basically, the
         rts          ;speed.
enddelay lda #$00
         sta chrptr
         ldx #$00
wrap1    lda $2a00,x  ;Copy the whole
                      ;char data and
         sta $2a40,x  ;paste it to $2040
         inx          ;8 times
         cpx #$08
         bne wrap1
         ldx #$00
wrap2    lda $2a08,x  ;Copy chars from
         sta $2a00,x  ;$2a08 to $2a00
         inx          ;for a perfect
         cpx #$40     ;working charset
         bne wrap2    ;animation.
         rts

Now the background animation and that is sorted out. We are going to do a little game. Actually I have done an example of a simple game, which uses both the background char animation and also the hardware sprite to background collision routine. The game is called "Give The Dog A Bone". The concept is pretty simple, and you'll see the example source below. Okay, so it is not really much of a game, but we'll look into expanding the game and the source code in the next chapter, which I'm sure you will find interesting.



SOURCE CODE

DOWNLOAD C64 STUDIO PROJECT, BINARY AND SOURCE


CHALLENGE 4:

You seen how basic this game is. Why not re-designing the same type of game, but with new graphics, sprites and music. 

Software related sprite/background Collision

Last time in this section, we were showing examples on making sprite/background collision, using the $D01F register. However, there's also another method (but longer method as well), which is the use of sprite/character collision. How is this usually done? Well, it is different compared to using $D01F. When we used $D01F, it could only detect a collision if a sprite hit a visible character. This method was used a lot, with some of my games, such as Balloonacy 1 and 2, Grid Zone, and a few others. However, we shall be taking a look at the software sprite/background collision - and even come up with a little example game for you to try out.

The software programmed sprite/collision register will detect whether or not a sprite hits a selected character value on screen. This is based on the Racked Off sprite/background collision routine by Kenho, and a few other people on the CSDB who has also contributed the routine on the CSDB forums. Before we start. We shall be making a simple game, here's my implementation of the routine (using the same square and line example) which will flash the square if it collides into the INVERTED SPACEBAR character.

SOURCE CODE (TASS Format)

Little quiz:

Taking a look at the example, above. Change the line from the inverted SPACEBAR character to a different character and then assemble and run the program to see what will happen. That's correct, no collision at all.
That is how the Software related sprite/background collision works, while $D01F uses any visible character on screen.

Okay folks it's now GAME MAKING time. Today we are very proud to bring you an example game called:

HYPER BLITZ
We are going to make a well known retro game, which may have been programmed in BASIC so many times either from books, magazines, toilet walls. Okay, maybe not. Anyway don't get too excited because we're pushing BLITZ even further and program our own. This is an example game which will be using the software related sprite/background collision feature, but this time round we are using this feature TWICE. Anyway, let me explain more about the game code before you decide - LET'S MAKE SOME BLITZ GAMES AND SHOW RICHARD WHAT WE CAN DO.



For a start off, before entering the machine code into Turbo Assembler or possibly one of the assemblers. Draw some sprites, the game charset. Make build your own level design and then compose some music (Unless you want to use my music of course). Now load in the assembler (or export what you done and place it where your cross assembler lies). Then load up the source. Feel free to modify it if you like :)

The source:

Just a quick explanation about what happens in the source (As the source is documented itself). We start the code by setting up the graphics type and colours. Simply by switching on the sprite/charset multicolour. Next comes the initializing of the IRQ interrupt routine. Where we try and hack a raster interrupt to work as a continues. After clearing the flag. We switch on only sprites, where the player is a space ship and the bullet is a simple bomb. After this, we create our own sync timer so, when an IRQ raster interrupt is playing, we can synchronize the timer to get all movements and subroutines synchronized. Then comes the JSR subroutines, which will do various checks. We also have subroutines that will constantly move the player until it hits one of the buildings. Routines to reposition the player once in a certain position off screen. Check for a sprite to char collision, according to whether the player hits the building or whether a bomb hits the building, making the play score points. There are also routines which will animate the player's ship as well.  And the finalize the code, we have the main IRQ raster interrupt in action as well. You best take a look at the source code to see what I mean.

SOURCE CODE

DOWNLOAD C64 STUDIO PROJECT SOURCE AND BINARY FILES


Object Spawning (Shape Changing)

When it comes to assembly programming. The Commodore 64 is limited to 8 sprites. However, when you play any C64 game, such as a shoot 'em up or any other classic / modern day C64 game. After a destroyed object (or an object leaves the screen) a new object is produced - or how I would like to call it, spawned. How is this done? Well, there are different ways in which you could spawn a new object/animation. Either produce a single table of bytes and compare a value of a table for each object or generate a self-mod table in which generates a new object. Here is a small example:

EXAMPLE: A sprite leaves the very right of the screen, then morphs into a new object

;MOVE SPRITE UNTIL IT LEAVES THE SCREEN. AFTER IT HAS
;LEFT THE SCREEN. TURN THE OBJECT INTO A DIFFERENT SPRITE

    !TO "SPRITESPAWN",CBM
    *=$0801
    !BASIC 2018,2064
    *=$0810
    SEI
    JSR $E544
    LDA #$01
    STA $D015
    STA $D01C
    STA $D020
    STA $D021
    LDA #<HEXAGON    ;Fetch lowbyte of HEXAGON
    STA SPRANIM1+1   ;Store to Self-Mod sprite anim
    LDA #>HEXAGON    ;Fetch hibyte of HEXAGON
    STA SPRANIM1+2   ;Store to Self-Mod sprite anim
    LDA #$06
    STA SPRCOLOUR+1  ;Store new colour to sprite
    LDA #$88
    STA OBJ_Position+1 ;Virtual Y sprite position
    LDA #$00
    STA OBJ_Position   ;Virtual X sprite position
;Create a test loop
TESTLOOP
    LDA #$32
    CMP $D012
    BNE *-3
    LDA OBJ_Position+1
    STA $D001
    LDA OBJ_Position
    ASL
    ROL $D010
    STA $D000
    JSR SPRITE0PROPERTIES
    JMP TESTLOOP

;Animate, display colour and also move sprite across the screen.
;As soon as the sprite has moved outside the screen into the border.
;Spawn a new sprite frame and colour, by reading the SPAWNNEXT
;subroutine. Then reposition the sprite horizontal position

SPRITE0PROPERTIES   

SPRANIM1
    LDA SQUARE    ;Default SQUARE for sprite type
    STA $07F8     ;store to SPRITE0 type
SPRCOLOUR
    LDA #7        ;Yellow square
MOVESPRITE1
    LDA OBJ_POSITION    ;Since expanded sprite movement,
    CLC                 ;move the sprite 1 byte to the right
    ADC #1              ;The speed for movement
    CMP #AC             ;Has the object left the screen?
    BCC UPDATERIGHT     ;Still below, update new sprite position
    JMP SPAWNNEXT       ;Jump to subroutine SPAWNNEXT to set new object properties
UPDATERIGHT
    STA OBJ_POSITION    ;Linked to movement, store the new position of object
    RTS
   
;Subroutine which will spawn a new object. First a pointer (Spawn Pointer) is read
;in order to read the next byte table SPAWNTABLE. SPAWNTABLE is a simple series of
;numbers in which will read a selected table position (according to the second counter
;SELECTPOINTER. Here's how it is done.

SPAWNNEXT
    LDX SPAWNPOINTER    ;Set Pointer SPAWNPOINTER (X Value)
    LDA SPAWNTABLE,X    ;Read a byte from SPAWTABLE
    STA SELECTPOINTER   ;Store it to the SELECTPOINTER
    INX                 ;Add 1 to value of SPAWNPOINTER (For reading the next table)
    CPX #16             ;Total number of bytes in random sequence (16)
    BEQ RESETSPAWNTABLE ;Yes. Reset the spawn table.
    INC SPAWNPOINTER    ;Otherwise increment the spawn pointer
    JMP SELECTNEXT

RESETSPAWNTABLE
    LDX #$00
    STX SPAWNPOINTER
    JMP SELECTNEXT

;Select the next object to be spawned in order to generate new
;sprite properties.

SELECTNEXT
    LDY SELECTPOINTER   ;Set pointer SELECTNEXT (Y Value)
    LDA SHAPELOW,Y      ;Select lo byte frame from table SHAPELOW
    STA SPRANIM1+1      ;Store byte from SHAPELOW into self-mod low byte of SPRANIM
    LDA SHAPEHI,Y       ;Select hi byte frame from table SHAPEHI
    STA SPRANIM1+2      ;Store byte from SHAPEHI into self-mod hi byte of SPRANIM
    LDA COLOURTABE,Y
    STA SPRCOLOUR,Y
    LDA #0              ;Reset X Position of sprite
    STA OBJ_POSITION
    INY
    RTS

;A couple of pointers for the subroutine (You can use Zeropage if you want)

OBJ_POSITION !BYTE 0,0
SPAWNPOINTER !BYTE 0
SELECTPOINTE !BYTE 0

;Series of tables to select each object.

SPAWNTABLE ;Selects the object type value (This will indicate
           ;object type for SELECTPOINTER (Which will pick
           ;one of the 4 specific objects)

    ;KEY:
    ;0 = Square
    ;1 = Circle
    ;2 = Triangle
    ;3 = Hexagon

    !BYTE 0,1,2,3,2,3,1,0
    !BYTE 2,1,3,2,1,3,1,2

SHAPELOW   ;Sets lowbyte of shape object (4 Objects)
    !BYTE <Square, <Circle, <Triangle, <Hexagon

SHAPEHI    ;Sets hibyte of shape object (4 Objects)
    !BYTE >Square, >Circle, >Traingle, >Hexagon

COLOURTABLE ;4 Objects - 4 colours
    !BYTE 2,7,5,6 ;Red, Yellow, Green, Blue

SQUARE
    !BYTE $80
CIRCLE
    !BYTE $81
TRIANGLE
    !BYTE $82
HEXAGON
    !BYTE $83


    *=$2000 ;Sprite objects
    !BIN "SHAPES.PRG",,2

Now, what if you wanted to play around with the speed of an object, or add more properties? Simple, all you would need to do is create some new tables (Depending what you are trying to do) - Then store to self-modifying code or pointers. The next game will show you how to do exactly that - and a few other tricks.

GAME 7: ROGUE NINJA

In this part of the tutorial, related to spawning new objects. A new example game (And it's a full game also) has been created. For a change it doesn't involve space ships and aliens. Instead, we have gone oriental with a simple Ninja platform (without sprite/char collision) game called Rogue Ninja. Written by Alf Yngve, and myself. Before I tell you what this game does and give you the source here are the instructions:

You are a Rogue Ninja, who has been captured by the evil master, Chow Mein. You have been thrown into his temple for a challenge. - A challenge of survival, skill and courage. 
. You have been chosen to face an army of ninjas and samurai's who are out to stop you. If you survive this ordeal, you will be set free. You start the game with 50 ninja stars, and a shield. After your shield runs out, you will no longer be invulnerable. Every time you lose a life, and respawn your ninja stars amount will NOT be restored. However help is at hand. Chests will magically appear on to the screen. Pick up these in order to gain 10 extra stars. You had better be quick otherwise they will disappear again. Try to survive as long as you can. 

Time to explain the layout of how the game code has been set out. Once again, I have set it out as a full game. It comes complete with front end presentation, main game, and ending. Also the game consists of two different tunes, one of which is used as the title music (Music at $9000), and in game music (Music at $1000). Self-mod pointers are set to play the correct tune every time you switch to the title and the game.

Now let's look at the code. At the start there are some variables, which control either the title or the game. Some are used as pointers, values or code position and some are used as zeropages.  The zeropages $02 and $03 have been chosen as the charset animation store pointer ($02) and the title screen smooth scrolling control ($03).

There are some additional values selected for the boxed sprite/sprite collision area. Should any sprite reach the range of the player or bullet, it will either kill the player, or kill the bullet (and/or possibly die, itself).

There are also some values to indicate the floor position for enemies or chest to appear. Some player and bullet speed properties also have been set. Enemy direction values (0 = left, 1 = right). There is code in the game that will check which direction enemies are facing.

Some screen pointers have also been set for the value of the font, which represents the custom numeric counters. Also some screen pointers for which area of the screen storing the values of the score should be made.

Next we add some PSEUDO commands in order to import the binary/PRG files of all of the graphics and music data. No !BASIC pseudo has been used this time, due to the memory location $0800 being used. So instead, I call Exomizer to decrunch and run at $4000. The memory has been set as follows:

- Game charset: $0800

- Title music: $1000

- Game sprites: $2000



- Game screen matrix (Which gets copied to the screen RAM): $2800



- Game screen colour attributes (Which gets copied to the colour RAM): $2C00

- Title screen and logo charset (combined): $3000


- Logo screen matrix: $3800



- Code and pointers: $4000

- In game music: $9000

The code starts by running the title screen. Then the main game killing off any existing interrupts to make a fresh start for every time the game refreshes. The main game code will draw the game screen, initialise a multi-raster interrupt, linking to each other, and of course init and play the music through a PAL/NTSC timer. 



A lot of the code is pretty much what you have already learned so far from previous chapters. So I won't need to go through everything, but there are some other things you may need to familiarise yourself to. This game has PROPERTIES for each object. They are the PLAYER, the BULLET, the ENEMIES and the CHEST. Each of the properties test for each sprite individually.



Player Properties (PlayerProperties)

The game starts with a check to see whether the player is alive or dead. This is indicated by using a conditional pointer, PlayerDead. If this value = 0 this is classed as FALSE. This will mean that the player is alive, and it will continue to the code which reads the player being alive. Otherwise, the code will jump straight to the player dead code.

Player Death Sequence (PlayerDead)

The code at PlayerDead, uses two pointers PlayerToss, which throws the player to the top of the screen, and PlayerFall, which changes the player's frame to player upside down, and falls through into the masked raster screen position. The lives counter is then decreased by one. If the lives counter = 0 then the GAME OVER routine is called (Which clears the screen, masks GAME OVER on screen and awaits the fire button to be pressed). If the number of lives are above 0 then the player is respawned to its original starting position (The central platform) with a shield enabled for a short time.

Player Alive (PlayerAlive)

The code at PlayerAlive will first call a collision calculation subroutine. This will setup the boxed collision boundary for the player and enemies. Also the player control checks pointer PlayerFalling. If this is true, then the player cannot pull down on the joystick, until the player has reached one of the set floor positions (See variables). Also the same effect occurs, if the player is still moving up, or has reached the very top floor. The control, up on joystick cannot be used. The player can still move left or right and throw stars (If he still has them). While the player is moving left or right, a pointer is set to a value of 0 or 1, indicating which direction the player is facing. PlayerDir is selected. Also according to the direction the player is moving (If not jumping/falling) a set animation pointer from the Animation code is stored to the player's sprite frame.

There is also a Fire Press function, which will test whether or not the player can throw ninja stars. There are two checks to this code. If the bullet object Y position (Obj_Position+3) is NOT at position 0. Then the bullet cannot be fired. Since it is still visible. Also if this is bypassed, a counter check is performed. The counter checks both digits of the Stars pointers. If Stars and Stars+1 equal 0, then the player cannot fire the stars. Otherwise if enough stars, and the stars are offset. A new star is spawned on to the player's sprite position. The direction of the player is also checked, for which direction the stars should fire. The PlayerDir pointer is copied to the BulletDir pointer. The number of stars is then deducted by 1. There's also some code to check whether the value of stars gets lower than 00 on both digits - if so, the No of stars = 00.

Player Jumping (DoJump)

A subroutine is called to make the player jump, until it reaches its specific destination floor. The player should jump from a lower floor to an upper floor. The JumpSource and JumpDest, FallSource, and FallDest is controlled by a Player Y position check subroutine. This will then store the new values of the player's Jump and Fall targets to pointers JumpSource, JumpDest, FallSource, FallDest. A frame is also set to show the back of the player. So it does look as if the player is actually jumping.

Player Falling (DoFall)

The player falling subroutine is called a similar way to making the player fall to the target floor position. A frame is also set to show the front of the player. So it does look as if the player is falling (Or has been repositioned).

Bullet Properties (BulletProperties)

The code BulletProperties sets up the properties of the player's bullet. It starts by calling a subroutine to test the bullet to enemy collision (Sets up the box-based sprite/sprite collision boundary as done before with the player properties). The code also checks whether or not the bullet is moving left or moving right. This is tested by checking the value of the pointer BulletDir. The bullet direction position is then calculated by moving the object, until the bullet has reached its limit before it is taken off screen.

Enemy Properties (EnemyProperties)

You may have remembered that with previous game builds, such as Missile Blasta, etc the game featured a loop, which killed each enemy, and masked a bullet as the explosion. Well, there is a much more better and feasible way to deal with enemy properties. Simply by giving EACH enemy an individual test. 5 subroutines have been called to test each of the enemies' properties. These are called as TestEnemy(x). Where (x) = the enemy value being tested.

Testing an enemy individually (TestEnemy(x))

For each subroutine. We start by testing whether or not a current enemy, which is being tested is dead. This is tested by calling a pointer Enemy(x)Dead and checking whether the value is true or false (0 or 1). For example if Enemy1Dead = 1 then call Enemy1IsDead. Otherwise call the enemy alive functions.

Enemy Dead subroutines (Enemy(x)IsDead))

Enemy1Dead was formed as 12. Therefore the command Enemy1IsDead was called. This doesn't really do much, apart from makes the enemy object fall from the game floor to the outer screen at a speed of 3. After the enemy has moved from the screen, it is placed at the top of the screen. Set to alive, and will move in the border, until it has reached the X target position outside the right border.

Enemy Alive subroutine (Enemy(x)Alive))

Enemy(x)Alive will test whether or not the player is already dead, before it checks the enemy to player collision. If the enemy is in range of the player then a collision is checked. If the player is invincible, then the enemy just stops moving if targeting the player - Unless the player moves away from it. Otherwise the player gets killed. However, if the enemy hits the boundary of the player's bullet sprite (the ninja star), a number of lives is decreased (dec Enemy(x)Lives)) and then checked whether or not the number of lives = 0. If the enemy lives = 0. Then Enemy(x)Hit subroutine is called to turn the sprite colour to RED, and then set Enemy(x)Dead to true (or 1). The player is awarded points. Every hit to the enemy homes the bullet off screen. If the enemy is not dead, a self-mod animation is used for the enemy frame and enemy colour. This is used by calling a table. Also MoveEnemy(x) subroutine is called to move the enemy across the screen.

Moving enemies (MoveEnemy(x)

Like with testing which direction the player faces, a test function is called to check for enemy direction. For example If Enemy1Dir = 1 then allow the enemy sprite to move right. Otherwise make it move LEFT. The enemy has to move to the outer screen before the next enemy can be spawned. JMP SpawnNextEnemy(x) shows this.

Spawning a new enemy (SpawnNextEnemy(x))

The subroutine SpawnNextEnemy1 calls a JSR routine to read from a series of spawn tables and store those as new object properties, via self-mod routines. A table is read to store a new colour (NewEnemyColour) for the enemy, it is then stored to a self-mod pointer (E1Colour+1). A new delay of the enemy speed (Enemy1DelayLimit), new floor position for where the enemy should start on (NewFloorPosition and SMStartXPosition). This is then stored to the actual object Y, and X position. Self-mod animation frames (SMFrameLow and SMFrameHi) the low+hi byte of those frames are stored to the selfmod enemy frame pointer at E1Frame, number of hits to kill (SMHitsToKill) which is stored to Enemy1Lives, scoring value (SMScoreValue), and direction of  enemy to read (SMDirection) is stored to Enemy1Dir. Also the enemy dead pointer should always be set to 0 after spawning has finished.

Main loop to spawn a new enemy (SpawnNewEnemy)

You may have remembered that in a previous chapter I showed you a subroutine that allowed to spawn different shapes. Well, in this game code I did something simular, but called two different loops. The first loop loads X to the SpawnPointer, reads the bytes from the table SpawnTable and stores it to an additional pointer SelectPointer. Then the loop continues to read other tables, which are EnemyColourTable (Stored to NewEnemyColour), EnemyDelayTable (Stored to NewEnemyTable), EnemyStartTable (Stored to NewFloorPosition). These set values for the colour, delayed speed and starting floor position for the next object spawned. The table is read 254 times. After 254 spawns, the game is complete (GameIsWon) and the Well Done screen appears.

The second loop is loaded as Y  from the SelectPointer, which reads the bytes of the tables from EnemyTypeTableLo, and stores to SMFrameLow (The low byte selfmod pointer for animation), EnemyTypeTableHi to SMFrameHi (The hi byte selfmod pointer for animation), EnemyKillTable to SMHitsToKill, EnemyScoreTable to SMScoreValue, EnemyDirTable to SMDirection, StartXTable to SMStartXPosition then finished.

Chest Properties

ChestCanSpawn is checked to see whether or not the chest is allowed to appear on not. Once again it uses a true/false check. If ChestCanSpawn = 1 then ChestCanAppear. Otherwise a timed delay through the pointer ChestWaitDelay is called. After 3 steps the ChestWaitDelay pointer will reset, and increment the ChestWaitTime, until the timer has reached value of 100. Then set a new position for the chest.

Setting the Chest position SetNewPosition

After the timer has finished, and reset. A loop is called to set a position of the chest, simply by reading the enemy start position (EnemyStartTable) and chest X start position table (ChestStartTable). These are then stored to the object position (Obj_Position+15, Obj_Position+14). The loop is then incremented by 1. A pointer ChestCanSpawn is then set to true.

Making the chest appear (ChestCanAppear)

The next subroutine is quite a clever one. As you might have guessed the chest is allowed to spawn, but it also calls its own animation subroutine. The explosion table is read, but during explosion. The chest to player collision cannot be read. However after the explosion has finished, and the chest has appeared. A collision subroutine (Similar to the player/enemy collision) is then called. After the player has made contact with the chest, it will simply disappear.

The ChestWaitDelay and ChestWaitTime command is called once again to keep the chest in play (unless the player touched it). After 20 intervals, the chest is moved off screen, and disables ChestCanSpawn. You would then have to wait another 100 iterations for the next chest to reposition and also appear. The chest only appears to the far left, middle or right of the screen. If the player hits the chest, a subroutine is called to increase the value of points and give 10 ninja stars to the player.

And that's practically it. :)

Have fun with the game, and the source code.

VIEW SOURCE


DOWNLOAD C64STUDIO BINARY+SOURCE DATA


SPRITE PROPERTIES - Using MACRO's

Whenever you want to write code for a game, and create things such as object properties. There are two different ways to go about writing individual properties for each object. You can either call multiple JSRs and type in the first object properties code (followed by RTS at the end of the code). Then copy and paste and rename your labels and set the properties for a next object. And so on.

There is also an alternative way in which this could be done - and probably a short cut, compared to copy/pasting and spending a long time to re-label your code. The !MACRO command, and set the macro values/properties for each item. Let us give out a simple example on setting out a sprite frame and colour for every sprite. We lay out a macro to do exactly that

!MACRO setsprite spritetype, selectedframe, frame, selectedcolour, colour {
    lda #selectedframe
    sta sprframe
    lda #selectedcolour
    sta sprcolour
    rts
}

Then you would need to call a command and properties that READS the macro

    +setsprite $80,$07f8,$0a,$d027

This piece of code calls to automatically set the frame of a sprite at $2000 and store it to the hardware sprite0 type, then set the colour and store it to sprite0 colour. Then exits the code inside a macro with an RTS. Works a charm. Now what if you wanted to do a similar code for the rest of the sprites, but set the next sprite type and colour without having to type long strings of code? Simply add another small command.

+setsprite $80,$07f8,$0a,$d027 ;Sprite 0 
+setsprite $81,$07f9,$0d,$d028 ;Sprite 1
+setsprite $82,$07fa,$03,$d029 ;Sprite 2
+setsprite $83,$07fb,$0e,$d02a ;Sprite 3
+setsprite $84,$07fc,$04,$d02b ;Sprite 4
+setsprite $85,$07fd,$07,$d02c ;Sprite 5
+setsprite $86,$07fe,$0f,$d02d ;Sprite 6
+setsprite $87,$07ff,$08,$d029 ;Sprite 7
           
Macro commands can also be expanded to do other things. with sprite properties. Remember the sprite movement example MSB we did earlier on (The expanded sprite exercise)? There is a loop in the code, which makes the sprites move, by reading a table X, Y and updating the speed X,Y of the moving sprite. Here is a new example, which uses a different method - The macros:

WHERE YOU FIND THIS CODE (From chapter ALLOWING SPRITES TO MOVE MORE THAN 256 PIXELS)....

                ;Move all sprites via a loop
MOVESUBR               
                LDX #$00
MOVESPRITES
                LDA VSPRPOS,X   ;Grab virtual sprite position from table
                CLC              
                ADC MOVETABLE,X ;Add a positive/negative value according to the movetable
                STA VSPRPOS,X   ;Store the value as a speed directly to the virtual sprite posiion
                INX             ;Add a value to X loop(1 byte)
                CPX #16         ;Have all 16 bytes of the speed table been read?
                BNE MOVESPRITES ;No, read next byte from table



RECREATE THE CODE ABOVE TO THIS:
;Move all sprites via an individual macro 

MOVESUBR

;An actual MACRO code, which will read the current position of a sprite,
;then reposition the sprite according to speed.

;HINT ... INSIDE !MACRO command ... NEVER add RTS at the end of the MACRO, otherwise the next
;code read will be ignored.

!MACRO movesprite posx,speedx,posy,speedy {
lda posx
sec
sbc speedx
sta posx
lda posy
clc
adc speedy
sta posy

}

;Calls macro routine (from above) to move all 8 sprites accordingly.
;Simply by reading the bytes of even values of VSPRPOS table, to indicate the X position,
;MOVETABLE (X position table), and ODD values of VSPRPOS table, to indicated the Y position
;and set the MOVETABLE+1 (Or odd value) for Y movement speed.

+movesprite VSPRPOS, MOVETABLE, VSPRPOS+1, MOVETABLE+1
+movesprite VSPRPOS+2, MOVETABLE+2, VSPRPOS+3, MOVETABLE+3
+movesprite VSPRPOS+4, MOVETABLE+4, VSPRPOS+5, MOVETABLE+5
+movesprite VSPRPOS+6, MOVETABLE+6, VSPRPOS+7, MOVETABLE+7
+movesprite VSPRPOS+8, MOVETABLE+8, VSPRPOS+9, MOVETABLE+9
+movesprite VSPRPOS+10, MOVETABLE+10, VSPRPOS+11, MOVETABLE+11
+movesprite VSPRPOS+12, MOVETABLE+12, VSPRPOS+13, MOVETABLE+13
+movesprite VSPRPOS+14, MOVETABLE+14, VSPRPOS+15, MOVETABLE+15
rts
GAME 3B: MISSILE BLASTA - REMASTERED V2

Following the previous source, I have decided to remaster Missile Blasta for the second time. There have been comments, and suggestions, in relation to improved game play. This example is going to do exactly that. So then what are we going to do. As you may have noticed with the build I produced earlier on. The game originally used the player bullet, as an explosion, also the missiles are moving at one particular speed, and are just moving downwards. You won't want that to happen all the time. This was how it looked before hand:



This new build is a final build - for this game that is. It is much different. It features brand new graphics, slightly more new music, some in game sound effects (which the data can be pushed through the Goat Tracker music player), which may interrupt the in game music when in use. Also there is much better gameplay. You will see after you have downloaded the game with source code. The game still uses the same concept as the example above. The missiles are larger and the second level will show a difference in game play.



So then, what about the code. How does it differ compared to the original Missile Blasta? In V1, the missiles movement, and collision were all linked to a loop inside a small subroutine. The bullet sprite was set to mask an explosion every time a missile was hit by it. The bullet was the removed and placed elsewhere on the screen. V2 is completely different. For each missile property, it's movement, animation and its collision, etc.  is controlled by a !MACRO command, and the pointers are linked to each missile. Here are some MACRO examples that you will find in the code:

EXAMPLE MACRO: Test Missile to Bullet Collision

    ;Tests whether a missile is already dead, if so. Ignore
    ;collision. Otherwise, check whether or not the bullet is
    ;in range of the missile. If it is within range of the
    ;collision area, the missile gets destroyed, and the player
    ;will score 100 points.

!macro missiletobullet missiledead, bulletx, bullety, speedx {

    lda missiledead    ;Is the missile alread destroyed?
    cmp #1                    ;Yes?
    beq .nomissilecollision ;Do not read missile collision

    lda bulletx                 ;bullet x range to missile
    cmp collision+$00        ;Left range
    bcc .nomissilecollision ;no collision
    cmp collision+$01        ;Right range
    bcs .nomissilecollision    ;no collision

    lda bullety                    ;bullet y range to missile
    cmp collision+$02        ;Top range
    bcc .nomissilecollision ;no collision
    cmp collision+$03        ;bottom range
    bcs .nomissilecollision    ;no collision
    lda #0
    sta spritepos+2 ;move bullet out of screen
    sta speedx            ;also stop missile speed x
    lda #1                    ;then destroy the missile.
    sta missiledead
    jsr playmissileexplosionsfx ;play explosion sound effects
    jsr doscore                                    ;call score adding routine.
.nomissilecollision
    }                 

A little example, linked to the macro

    +missiletobullet missile6dead, spritepos+14, spritepos+15, missile6speed
    rts

What this does is calls the macro, linked to the macro labels (Next to !MACRO) that helps you shorten the code to give each missile an independent property. The one above tests the collision of each missile. A quick key will tell you what each macro property for this example above looks like:

+missiletobullet - This is the macro which is being called inside a routine
missile6dead = missiledead - This is the pointer missile6dead being used to check if the missile is destroyed or not
spritepos+14 = bulletx - The missile sprite 8 x position, to be read by the player's bullet collision
spritepos+15 = bullety - The missile sprite 8 y position, to be read by the player's bullet collision
missile6speed = speedx - Horizontal speed of the missile if it is to be moving across the game screen. If hit by the player bullet it will be zeroed in order to stop moving the missile.

MACRO 2: Missile to Player

!macro missiletoplayer missiledead, spritex, spritey {
    lda missiledead    ;is the missile already dead?
    cmp #1                    ;yes? ...
    beq .noplayercollision                         ;no player collision

    lda spritex                ;read collision of missile x pos
    cmp collision+$04    ;check collision boundary left
    bcc .noplayercollision
    cmp collision+$05    ;check collision boundary right
    bcs .noplayercollision
    lda spritey                ;read collision of missile y pos
    cmp collision+$06    ;check collision boundary top
    bcc .noplayercollision
    cmp collision+$07 ;check collision boundary bottom
    bcs .noplayercollision
    jsr playplayershieldhitsfx ;play player shield hit
    lda #1
    sta missiledead

    jmp loseshield        ;exit macro and make player lose
}

m6p
+missiletoplayer missile6dead, objpos+14, objpos+15
rts

After the macro code, you will discover a few JSR commands, followed by RTS. This is so that each independant missile is operating correctly. When everything is put together without the JSR commands, for some strange reason the game goes out of sync, due to too perhaps, too many cycles from the C64 being wasted. Like with the example before hand. Here's what each pointer to macro label represents

missiletoplayer = Macro that is being called. Which should link the missile to player collision.
missile6dead = missiledead - Missile 6 death pointer (Is the missile dead)
spritepos+14 = spritex - Virtual X sprite position of missile 6
spritepos+15 = spritey - Virtual Y sprite position of missile 6

MACRO Example 3 - Object properties

The next macro tests properties / animation, position/movment and speed of a chosen missile

!macro missile_properties missiledead, explodedelay, explodepointer, sprframe, missilex, speedx, missiley, speedy, occupiedposition {

lda missiledead ;is the missile already dead?
    cmp #1                     ;yes ...
    bne .missileok     ;else continue to missile move code
                                      ;otherwise do enemy death animation
    lda explodedelay
    cmp #$03
    beq .resetexplode
    inc explodedelay
    jmp .exit

.resetexplode
    lda #$00
    sta explodedelay
    ldx explodepointer
    lda expltbl,x
    sta sprframe
    inx
    cpx #8
    beq .explodeend
    inc explodepointer
    rts
.explodeend ;reset explosion for the next enemy death.
    ldx #0
    stx explodepointer
    lda #0
    sta missiley
    lda #0
    sta missiledead
    jmp .updatemissilex

    ;the missile is not dead, so we can move the missiles
    ;according to selected speed from tables.

.missileok
    lda missileframe ;grab animation frame for missile
    sta sprframe         
    ;store it to selected sprite frame
    lda missiley          ;grab y position of missile sprite
    clc
    adc speedy             ;move it according to speed counter
    cmp #$12                 ;below position #12 should call a
    bcs .updatemissiley ;update Y position of missile

             ;randomizer subroutine to select next X position of missile            
.newposition
    jsr random             ;position/sprite properties
    lda ranyspeed         ;set random speed from table for y missiles
    sta speedy                
    lda ranxspeed         ;set random speed for x missiles
    sta speedx
.occupytest                   
    ldy #$00
.occupyloop
    lda ranpos
    cmp xposoccupied,y ;Is position already occupied by another missile?
    bne .occupynext             ;Yes it is ... Else, NO store new position.
    jsr random
    jmp .occupytest
.occupynext
    iny
    cpy #6
    bne .occupyloop
.updatemissilex
    sta missilex         ;then set new position of sprite
    sta occupiedposition
    lda #$12                 ;and reset y position of missile
.updatemissiley                   
    sta missiley                 ;update new y position of missile
.exit

} ;end of macro

m6props    ;Linked to macro: Missile 6 properties

    +missile_properties missile6dead, missile6expdelay, missile6exppointer, $07ff, spritepos+14, missile6speed, spritepos+15, missile6speed+1, xposoccupied+5

    rts             

missile_properties = MACRO being called in the game code
missile6dead = missiledead  -  pointer to check whether or not a missile is already destroyed
missile6expdelay = explodedelay - delay of missile explosion animation
missile6exppointer = explodepointer - counter of missile explosion
$07ff = sprframe - sprite frame through hardware selected to animate
spritepos+14 = missilex - missile X position
missile6speed = speedx - x-speed of missile
spritepos+15 = missiley - missile Y position
missile6speed+1 = speedy - y speed of missile
xposoccupied+5 = occupiedposition

The additional xposoccupied code is used in order to try and prevent a non-rocking missile from appearing on the same Y position as any other missiles.

MACRO Example 4: Rocking missiles

Although we have straight moving missiles from the very first Missile Blasta. The game play needs to be somewhat better. Therefore I have added a subroutine, which uses a CUSTOM movement pattern. Known as a TIMED MOVEMENT. You might have noticed I used a similar method with Starfysh, but the code for Starfysh was slightly more complex. I have chosen a simple rocking method for this game, which uses yet another macro and also a couple more pointers. The rocking missiles are based on an individual timer, then after an interval has expired, the timer is reset. The missile then changes its direction. The X-Speed of a rocker varies, according to a randomizer and level of course.

    !macro rockingmissile missiledead, missiletime, direction, missilex, speedx {

    lda missiledead    ;is the missile already destroyed?
    cmp #1                     ;yes ... leave it and then
    beq .skipxtimer    ;exit the code.

    jsr .readtime    ;call subroutine to set time calculation

    lda direction        ;which direction are the missiles moving?
    cmp #1                    ;right? ...
    beq .moveright    ;yes, they are moving right ... else:

    lda missilex        ;code to make individual missile move
    sec                            ;to the left.
    sbc speedx
    cmp #$0c              ;does the missile reach the very left
    bcs .missileleftok

    jmp .timeexpired

.missileleftok   
    sta missilex         ;of the screen?
    jmp .readtime

.moveright
    lda missilex        ;code to make individual missile move
    clc                            ;to the right.
    adc speedx
    cmp #$a2                ;does the missile reach the end of
    ;the screen? if so, change direction
    bcc .missilerightok
    jmp .timeexpired


.missilerightok
    sta missilex

.readtime                    ;timer code (calculates time, and also
    lda missiletime    ;switches direction the sprites move.
    cmp #$70
    beq .timeexpired
    inc missiletime
    jmp .skipxtimer

.timeexpired             ;missile time reset and direction checked
    lda #0
    sta missiletime
    lda direction       
    cmp #1                    ;direction right?
    beq .setleft    ;yes, right. Switch to LEFT!
    lda #1
    sta direction
    jmp .skipxtimer
.setleft
    lda #0
    sta direction
.skipxtimer

    }   
    ;call the pointers in which control the value of the
    ;rockers - for some reason without the jsr's the macro
    ;combined goes really crazy :)
    ;as a failsafe, add multiple jsrs for the rocking missiles
    ;code.

Another snippet with info for you:

rocker6
    +rockingmissile missile6dead, missile6xtime, missile6direction, spritepos+14, missile6speed
    rts

rockingmissile
= Macro being used to call the independant rocking missile code
missile6dead = missiledead - Death pointer to check if chosen missile is already dead
missile6xtime = missiletime - Selected pointer which calculates the interval of a missile direction
missile6direction = direction  - Which way is the X direction for missile going to be 0 = Left, 1 = Right
spritepos+14 = missilex - Missile X position
missile6speed = speed - The speed of the rocking to take place

The macro code might be quite confusing at first, but eventually you should get used to it. I strongly recommend that in order to understand this game code. You spend a while studying the code, especially the MACRO.

CREATING LEVELS:

I showed you some macros, but what about creating levels in this game? Well, it has been simply done by creating a series of tables, which tweaks the game play slightly. The code for creating new levels look something like this:

A level pointer is used as the loop, which reads a series of bytes from the low byte and hibyte table of level data, then stores it into a self-modifying level set up code. The background mulicolour scheme is changed according to the level which the player is at. Once the pointer has reached 9 (Since there are 8 levels in total), the game is finished.  After the low and high bytes of level tables (For example x-speed of missile, and y-speed of missile) have been stored respectably to the specific self-mod pointers. The selected X / Y random table is written to the random table ranxspeed, ranyspeed. 

levelsetup

    ldx levelpointer    ;Read pointer position according to level
    lda levelxtbllo,x    ;Read low-byte of xspeed table for missiles
    sta levelsm1+1        ;store to selfmod low-byte for random xspeed of missile
    lda levelxtblhi,x ;Read hi-byte of xspeed table for missiles
    sta levelsm1+2        ;store to selfmod hi-byte for random yspeed
    lda levelytbllo,x ;Read low-byte of yspeed table for missiles
    sta levelsm2+1        ;store to selfmod low-byte for random yspeed
    lda levelytblhi,x    ;Read hi-byte of yspeed table for missiles
    sta levelsm2+2      ;store to selfmod hi-byte for random missiles
    lda mcol1,x             ;Read selected byte from level colour table 1
    sta $d022                    ;then place it to char multicolour #1
    lda mcol2,x                ;Read selected byte from level colour table 2
    sta $d023                    ;then place it to char multicolour #2
    inx
    cpx #9 ;Level over - No more levels to play!
    beq gamecomplete
    inc levelpointer    ;Increment next position of level pointer

    ;Simple self-mod code, for setting up the level speed table
    ;for the x and y speed of missiles

    ldy #$00
levelsm1
    lda $ffff,y ;Self-mod position for missile randomxspeed
    sta ranxspeed,y
levelsm2   
    lda $ffff,y ;Self-mod position for missile randomyspeed
    sta ranyspeed,y
    iny
    cpy #24
    bne levelsm1
gamecomplete                   
    rts

Look further into the whole source code to see how the level setup is linked to the level counter, and how the game is completed.
   
GoatTracker SFX Support

This game also features additional  sound effects support for Goat Tracker V2 tunes. The in game music uses a 3 channel sound. Although if you decide to write music. I would strongly recommend a 1 or 2 channel sound is used. INS2SND was used in order to generate a table for the custom sound effects, which can play/interrupt a channel inside the music, Here's a small example: The player shooting a bullet.

    ;Play sound effect - Player shooting a laser

playplayershootsfx
    lda #<shootsfx                ;Low byte of sound
    ldy #>shootsfx                ;Hi byte of sound
    ldx #7                        ;Sound channel (2)
    jsr sfxplay                   ;Play SFX
    rts

Table to match it:

;Sound for player shooting                 
shootsfx                 
    !byte $0E,$EE,$00,$CC,$21,$cc,$ca,$c8,$c6,$c4,$c2,$10
    !byte $90,$00

The first 2 bytes represent attack/decay, sustain/release, pulse, and then a series of values are detected. $90-$ff represent the octave of the sound effects, and $11, $21, $31, $41, $51 ,$61 represent the wave form for the sound effects. INS2SND doesn't always need to be used if you know what type of SFX you can create. End the byte SFX with a 0.

And that's basically the important bits in the game. As mentioned before, I strongly recommend you take a look at the whole source code in C64Studio, in order to know what is happening. The code is briefly self-explained and shows what is happening.

Download C64Studio V5.7a Project and Binary data

View source code


CHALLENGE 4:

Try to make an EVEN better version of this type of game, by building some new graphics, using more animation frames and also try to build a new background for each level (and set it up by modifying the level setup code).


Next Page / Previous Page / Back to Contents Page