Sprite Wrapping
Posted: Fri Aug 02, 2019 2:15 pm
Hi All,
I've been working on learning how to wrap sprites around the screen. The effect being that when the sprite starts to leave one side of the screen it begins to appear on the opposite.
My example code works in the y-axis (I think x-axis is going to be tricky) and moves 1 pixel at a time either upwards or downwards. To change direction just press the D key. I'm sure it's not the best or smartess solution but, it works. Hope you like it.
I've been working on learning how to wrap sprites around the screen. The effect being that when the sprite starts to leave one side of the screen it begins to appear on the opposite.
My example code works in the y-axis (I think x-axis is going to be tricky) and moves 1 pixel at a time either upwards or downwards. To change direction just press the D key. I'm sure it's not the best or smartess solution but, it works. Hope you like it.
Code: Select all
xmin equ 0
xmax equ 319
ymin equ 0
ymax equ 199
spriteH equ 18 ;value in total scanlines i.e pixel height
spriteW equ 4 ;value in bytes in the x axis on one scanline
org &4000
ld a,1 ;screen mode
call &BC0E
ld b,0 ;screen background colour
ld c,b
call &BC32
ld b,2 ;screen border colour
ld c,b
call &BC38
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mainloop ;start of main loop
ld a,66 ;ESC key. 66=ESC key code
call &BB1E ;key press check
jp nz,finish ;if ESC key pressed, exit to BASIC
ld a,61 ;D key
call &BB1E ;key press check
call nz,changedir ;if pressed, perform vertical direction change
call updateplayermovements
call drawscreenobjects
call &BD19 ;wait for v-sync
;call &bb18 ;wait for any key press
jr mainloop
finish:
ret ;back to BASIC
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
updateplayermovements
call updatespritepos ;update new sprite x/y position
ret
drawscreenobjects
call drawplayer
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
changedir:
ld a,(dir) ;get current dir flag
xor 1 ;flip the 1 to 0 or vice versa to change directional flag
ld (dir),a ;save flag back to memory
ret
updatespritepos:
ld hl,(ypos) ;get current ypos
ld a,(dir) ;load direction flag
cp 0 ;if it's 0 then we go in a downwards direction
jr z,_sub
cp 1 ;if it's 1 then we go in an upwards direction
jr z,_add
ret ;don't really need this return but, just in case something slips through
_sub
ld a,l ;get current ypos
cp ymin ;have we reached bottom of screen?
jr z,__resettotop ;if we have, reset pos to top
dec hl ;decrement ypos by one
jr _save
__resettotop
ld hl,ymax ;set ypos to ymax amount
jr _save
_add
ld a,l ;get current ypos
cp ymax ;have we reached top of screen?
jr z,__resettobottom ;if we have, reset pos to bottom
inc hl ;increment ypos by one
jr _save
__resettobottom
ld hl,ymin ;set ypos to ymin amount
jr _save
_save
ld (ypos),hl ;save new ypos
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
drawplayer:
ld de,(xpos) ;load in current xpos in pixels
ld hl,(ypos) ;load in current ypos in pixels
call &bc1d ;convert x/y to screen address, which gets saved in HL. AF & DE get corrupt
ld (scrmempos),hl ;save copy of the initial start screen address
;The lines below are like a function call whereby we pass it 3 parameters
;
ld hl, sprite_bar ;param 1 - load in sprite data
ld de,(scrmempos) ;param 2 - load in screen start memory pos
ld a, spriteH ;param 3 - height of sprite (in pixel lines)
call drawobject ;call the function to draw the sprite
ret
drawobject:
;Entry needs to be as follows;
;
; HL = Sprite data
; DE = Screen start address
; A = Sprite height in pixels
_plotline
ld bc,spriteW ;max horizontal byte count for scanline (loop)
push af ;save the sprite height count
and c ;reset the Zero flag because we need to test against it to
;see if we have reached the maximum horizontal byte count of the scanline
ldir ;blit the scanline data to the screen
pop af ;read back the height counter i.e current value
dec a ;reduce the height loop counter by one
cp &0 ;has the counter reached Zero?
ret z ;if yes, the sprite has been fully plotted, return to calling function
push af ;if not, save the current scanline counter value so we don't loose track..
;..of which scanline we're at in the sprite plot
__updatescradd: ;this block is to move to the next (sprite) scanline
ex de,hl ;we need to swap the two values around because we need DE's value in HL
ld hl,(scrmempos) ;get last scanline start address
___bottomcheck:
ld a,h ;lets check if the new scanline has reached the bottom of the screen
cp &ff ;if the new value is &FF then we have reached the bottom screen boundary
jp nz,_ok ;we use the Z flag to decide what we need to do based on the evaluation above
;if we haven't hit the bottom then skip rest of the code and check if we hit top
;of screen
ld a,l ;if the first check has been met then we have to check the low byte as well
cp &80 ;we compare this to &80 as that is the beginning of the last scanline low byte
jp c,_ok ;we check against the C flag as we want to perform a greater than check..
;..because the value can potentially be between &80 and &CF i.e
;left hand side of the screen to the right hand side of the screen
;again, if this condition is not met then resume as normal
;the block below does the sprite "wrap" by subtracting &3F80 (amount to take in back to the top)
;from current scanline address
ld a,h ;load H value into A
sub a,&3f ;subtract &3F
ld h,a ;save new H value back
ld a,l ;load L into A
sub a,&80 ;subtract &80
ld l,a ;save new L value back
jr _ok
___topcheck:
ld a,h ;lets check if the new scanline will exceed the bottom of the screen
cp &c0 ;if the new value is &00 then we have crossed the bottom screen boundary
jp nz,_ok ;we use the Z flag to decide what we need to do based on the evaluation above
;if we haven't hit the bottom then skip rest of the code and resume as normal
ld a,l ;if the first check has been met then we have to check the low byte as well
cp &4f ;we compare this to &80 as that is the beginning of the last scanline low byte
jp nc,_ok ;we check against the C flag as we want to perform a less than check..
;..because the value can potentially be between &00 and &4F i.e
;left hand side of the screen to the right hand side of the screen
;again, if this condition is not met then resume as normal
;the block below does the sprite "wrap" by subtracting &3F80 (amount to take in back to the top)
;from current scanline address
ld a,h ;load H value into A
add a,&3f ;subtract &3F
ld h,a ;save new H value back
ld a,l ;load L into A
add a,&80 ;subtract &80
ld l,a
_ok:
call &bc26 ;get next scanline start address based on current HL value. AF gets corrupt
pop af ;restore the scanline counter value
ld (scrmempos),hl ;save new scanline address for use in the blit
ex de,hl ;(re)swap the values around
jr _plotline ;plot scanline
ret
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;various object declarations/definitions
xpos: defw 150 ;sprite start x-pos
ypos: defw spriteH ;sprite start y-pos
dir: defb 1 ;player direction flag 0=downwards 1=upwards
scrmempos: defw 1 ;used to store screen memory start location.
;this is important as the whole sprite print logic works
;relative to this
nolist
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;various sprite data
sprite_Bar:
defb &00,&00,&00,&00; line 0
defb &00,&FF,&FF,&00; line 1
defb &00,&FF,&FF,&00; line 1
defb &33,&FF,&ff,&cc; line 3
defb &33,&FF,&FF,&cc; line 4
defb &00,&FF,&FF,&00; line 2
defb &00,&FF,&FF,&00; line 2
defb &00,&FF,&FF,&00; line 2
defb &00,&FF,&FF,&00; line 2
defb &00,&FF,&FF,&00; line 2
defb &00,&FF,&FF,&00; line 2
defb &00,&FF,&FF,&00; line 2
defb &00,&FF,&FF,&00; line 2
defb &33,&FF,&ff,&cc; line 3
defb &33,&FF,&FF,&cc; line 4
defb &00,&FF,&FF,&00; line 5
defb &00,&FF,&FF,&00; line 6
defb &00,&00,&00,&00; line 7