







SPRITES, OBJECTS AND PROGRAMMING YOUR OWN SIMPLE GAMES

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.





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

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 :)





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.







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)





;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:



;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*=$0810SEIJSR $E544LDA #$0C ;Grey borderSTA $D020LDA #$0B ;Grey backgroundSTA $D021LDA #%11111111 ;Enable all sprites (You can use #$FF or 255 if your wish)STA $D015LDA #%00000000 ;Disable other sprite properties which are:STA $D017 ;Sprite Y-EXPANSIONSTA $D01B ;Sprite behind backgroundSTA $D01D ;Sprite X-EXPANDSION;Draw all sprites as same sprite from $2000LDX #$00DRAWSPRITESLDA #$80 ;Frame to readSTA $07F8,X ;SPRITE0-7 TYPELDA #$01STA $D027,X ;SPRITE0-7 COLOURINXCPX #$08BNE DRAWSPRITES;Now sprites have been drawn. Set a default position where all sprites;a placed at the centre of the screen.LDX #$00POSITIONSPRITESLDA #$58 ;SELECTED X POSITIONSTA VSPRPOS,X ;SPRITE0-SPRITE7 X POSITION SET via custom;sprite positioning table (VSPRPOS)LDA #$88 ;SELECTED Y POSITIINSTA VSPRPOS+1,XINX ;ADD 2 TO READ TO THE NEXT POSITIONINXCPX #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 tableLOOPLDA #$FA ;RASTERLINECMP $D012BNE LOOPJSR EXPANDMSB ;Subroutine to expand MSB sprite areaJSR MOVESUBR ;Subroutine to move sprites by calculating movementJMP LOOP;Expand the position of the sprites, according to the;position of a proposed sprite position then store;the pointers to the hardware positionEXPANDMSBLDX #$00 ;Start of a loop for reading tablesEXPANDREADLDA VSPRPOS+1,X ;Grab the next current table position of VSPRPOSSTA $D001,X ;Store it to Sprite Y Hard ware spriteLDA VSPRPOS,X ;Grab the previous current table position of VSPRPOSASL ;16 bit multiplicationROR $D010 ;Expand the sprite position screenSTA $D000,X ;Store the new position to hardware Sprite XINX ;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 tableRTS ;Finished;Move all sprites via a loopMOVESUBRLDX #$00MOVESPRITESLDA VSPRPOS,X ;Grab virtual sprite position from tableCLCADC MOVETABLE,X ;Add a positive/negative value according to the movetableSTA VSPRPOS,X ;Store the value as a speed directly to the virtual sprite posiionINX ;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 tableJMP LOOP ;Exit subroutine;Sprite position table (VSPRPOS) - Virtual Sprite PositionVSPRPOS!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 tableMOVETABLE!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" Example 2 - The sprite pointers to hardware method for a single spriteExample 3 - Using MORE sprites to hardware sprite position method (USE JSR EXPANDMSB inside your program loop in an IRQ or Rasterline):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)

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.



