====== Character bullets ====== by Achim Here're a few basic ideas on how to handle character bullets. Using character bullets is a bit annoying, but it's the easiest way to save sprites. ===== Preperation ===== First thing to do is to set up a lookup table that holds the screen addresses for each screen row. This will make the movements a lot easier. ;create lookup table for screen rows lda #$00 sta tmp0 ;zp address sta tmp0+1 ;zp address+1 sta rowlow ;first row sta rowhi tax lookup: lda tmp0 clc adc #40 sta rowlow+1,x sta tmp0 bcc + inc tmp0+1 + lda tmp0+1 sta rowhi+1,x inx cpx #24 ;table for all 25 screen rows bne lookup rowlow: !fill 25,0 rowhi: !fill 25,0 'Rowhi' holds relative values (0 - 3) to make it versatile, e.g. for double buffering. A simple 'ora #screen_hibyte' will switch to the required screen buffer. Now you can allocate x/y-coordinates to your bullets: bullet_column = x, bullet_row = y Set up two tables for x and y (columns and rows): bullet_row: !fill 8,$ff ; =y coordinate, 8 bullets, $ff=bullet not active bullet_column: !fill 8,0 ; =x coordinate From now on you only have to manipulate the coordinates to move the bullets. Everytime your game engine refreshes the screen data, use 'bullet_row' as an index to fetch the required screen address: ;x-reg pointing at current char bullet ldy bullet_row,x ;fetch current screen position lda rowlow,y sta tmp0 ;zp address lda rowhi,y ora #screen_screenhibyte ;hi-byte screen buffer, e.g. $40 = screnn buffer located at $4000 sta tmp0+1 ;zp address+1 ldy bullet_column,x ;lda (tmp0),y ;to read screen address... or ;sta (tmp0),y ;to print on screen... ===== Examples ===== 1. A simple example would be a game with following specs: * one screen buffer * no scrolling * bullets moving only left or right A procedure would look like this: - check if bullet can be printed on screen - save char underneath char-bullet - save colorRAM of original char - print char on screen and add color - determine bullet direction So we need five tables: bullet_row: !fill 8,$ff ;8 bullets, row=$ff --> bullet inactive bullet_column: !fill 8,0 bullet_direction: !fill 8,0 ;e.g.: left=00, right<>00 charactertmp: !fill 8,0 ;stored char underneath bullet colortmp: !fill 8,0 ;stored color of char underneath char bullet In this case bullet_row=$ff means char bullet is not active and slot can be used. Each bullet is switched off by setting bullet_row to $ff. print a new bullet on screen: newbullet: ;called if player presses fire ldx #7 ;maximum of 8 bullets - lda bullet_row,x ;check if there's a free slot bmi setbullet dex bpl - rts setbullet: lda player_row ;precalculated screen row (=y-coordinate) of player sta bullet_row,x ;adjust this value if necessary, e.g. +/- 1 row lda player_column ;precalculated screen column (=x-coordinate) of player... sta bullet_column,x ;...adjust this value if necessary, e.g. +/- 1 column ldy bullet_row,x ;= y-coordinate lda rowlow,y ;fetch corresponding screen address sta tmp0 ;zp address lda rowhi,y ora #screen_hibyte ;hibyte of screen buffer sta tmp0+1 printbullet: ldy bullet_column,x ;= x-coordinate lda (tmp0),y ;read char underneath bullet... sta chararctertmp,x ;...and store it lda #bullet ;value of char bullet sta (tmp0),y ;print bullet on screen lda tmp0+1 ;switch to colorRAM and #$03 ora #$d8 sta tmp0+1 lda (tmp0),y ;read color of original char sta colortmp,x ;and store it lda #bulletcolor ;color of char bullet sta (tmp0),y ;set color rts Moving the bullets: ldx #7 - lda bullet_row,x bmi skip jsr move1bullet skip dex bpl - rts move1bullet: ;restore char and color of orignal char first ldy bullet_row,x ;= y-coordinate lda rowlow,y ;fetch corresponding screen address sta tmp0 lda rowhi,y ora #screen_hibyte ;hibyte of screen buffer sta tmp0+1 ldy bullet_column,x ;= x-coordinate lda chararctertmp,x ;restore original char sta (tmp0),y lda tmp0+1 ;switch to colorRAM and #$03 ora #$d8 sta tmp0+1 lda colortmp,x ;restore color sta (tmp0),y ;now move the bullet lda bullet_direction,x beq movebullet2left ;00=move to the left inc bullet_column,x lda bullet_column,x cmp #40 ;hitting right border? bcc + lda #$ff ;switch off bullet sta bullet_row,x rts + jmp printbullet ;same as above... movebullet2left: dec bullet_column,x bpl + lda #$ff ;hitting left border? sta bullet_row,x ;switch off bullet rts + jmp printbullet ;same as above ===== Multiple directions ===== 'Bullet_direction' can be used for at least 8 different directions. Idea would be to use the lower four bits for up (bit 0), down (bit 1), left (bit 2) and right (bit 3), similar to the joystick values you get from $dc00. Moving a bullet in 8 directions could look like this: ;x-reg pointing at current bullet movebulletup: lda bullet_direction,x and #1 ;bit 0 set? beq movebulletdown dec bullet_row,x bpl movebulletleft ;hitting top border? if not check left & right lda #$ff sta bullet_row,x ;then switch off bullet bmi movebulletleft ;continue with left&right movebulletdown: lda bullet_direction,x and #2 ;bit 1 set? beq movebulletleft inc bullet_row,x lda bullet_row,x cmp #25 ;hitting bottom border? bcc movebulletleft lda #$ff ;then switch off bullet sta bullet_row,x ;and continue with left&right movebulletleft: lda bullet_direction,x and #4 ;bit 2 set? beq movebulletright dec bullet_column,x bpl + lda #$ff ;hitting left border? sta bullet_row,x ;switch off bullet + rts movebulletright: lda bullet_direction,x and #8 ;bit 3 set? beq + inc bullet_column,x lda bullet_column,x cmp #40 ;hitting right border? bcc + lda #$ff ;switch off bullet sta bullet_row,x + rts Manipulate the coordinates like this and then again refresh the screen data ('printbullet', see above) ===== Scrolling screen ===== Scrolling the screen data automatically moves the char bullets as well, of course. The effect will be a line of char bullets on screen. So the game engine has to delete the char bullets left behind. This should be done when the hardscrolling is finished. Read the bullet tables to figue out which bullet is on screen. Adjust the coordinates according to the scrolling direction and restore the original char of the backdrop.