Re: Beginner's Sprite Tutorial
Posted: Mon Jan 01, 2018 1:38 am
I've now got as far as preshifting in my tutorial series, in a bit of a scattershod way.
Happy new year!
Happy new year!
The community forum for all Sinclair users
https://spectrumcomputing.co.uk/forums/
Which bit? The sprite code I posted on page 3 is assembly language that the ZX-Spin 0.666 assembler translates into machine code. There are no nobs & whistles so I think any assembler including ZEUS should be fine with it
no, from a different section of this site. and i used zx spin 0.666PeterJ wrote: ↑Tue Mar 27, 2018 6:50 pm @MrPixel have you got your simple example working in Zeus first? Zeus has what I call configuration options before the code. @Seven.FFF explains it very well in his tutorial. Get that working first.
Did you write that previous piece of code yourself and do you have coding experience in assembler from your Atari time?
Yes, for sure. The documentation is distributed on a non-linear fashion between here and here.
Code: Select all
zeusemulate "48K"
Code: Select all
zoLogicOperatorsHighPri = false
Code: Select all
zoSupportStringEscapes = false
Code: Select all
Zeus_PC = $nnnn
Stack equ $nnnn
Code: Select all
org $nnnn
Code: Select all
org zeuspage(7)
Code: Select all
output_z80"..\bin\blah.z80", $0000, EntryPoint
output_sna"..\bin\blah.sna", $0000, EntryPoint
output_tap"..\bin\blah.tap", "LoaderProgramName", "Comment", StartAddress, BytesToSave, 2, EntryPoint
output_tzx "..\bin\blah.tzx", "LoaderProgramName", "Comment", StartAddress, BytesToSave, 2, EntryPoint
Code: Select all
output_tap"..\bin\blah.tap", "LoaderProgramName", "Comment", StartAddressOfBlock, BytesOfBlockToSave, 3, EntryPoint, BorderColours
output_tap_block "..\bin\blah.tap",StartAddressOfSecondBlock, BytesOfSecondBlockToSave
output_tap_block "..\bin\blah.tap",StartAddressOfThirdBlock, BytesOfThirdBlockToSave
Code: Select all
import_bin "loading.scr", $4000
Cheers. It's good to see the code being useful.Freespirit wrote: ↑Thu Jul 02, 2020 8:41 pm Thanks for the code to print a sprite. I've created my own sprite and added to the code. My issue is my sprite is only 2 bytes wide, I have changed everything to allow for this but there is something is the 'yx2pix' call that I think only allows for 3 bytes wide sprites. Does anyone know what the code change would be for just 2 bytes wide? It's a bit complicated for me to follow at the moment. My sprite does move along but only a character at a time, something in that call stops in doing pixel movement.
Code: Select all
;
deletesprite: ;we need to delete the old sprite before we draw the new one. The sprite is 3 bytes wide & 16 pixels high
ld a,(x_coordinate) ;make C=xcor and B=ycor
ld c,a
ld a,(y_coordinate)
ld b,a
call yx2pix ;point DE at the corresponding screen address
ld b,16 ;sprite is 16 lines high
deleteloop: ;NEW!
ld a,0 ;empty A to delete
ld (de),a ;repeat a total of 3 times
inc e ;next column along
ld (de),a
dec e ;NEW
call nextlinedown ;move DE down one line
djnz deleteloop ;repeat 16 times
ret
;
Code: Select all
;
drawsprite:
ld a,(x_coordinate) ;make C=xcor and B=ycor
ld c,a
ld a,(y_coordinate)
ld b,a
call yx2pix ;point DE at corresponding screen position
ld a,(x_coordinate) ;but we still need to find which preshifted sprite to draw
and 00000111b ;we have 8 preshifted graphics to choose from, cycled 0-7 in the right hand 3 bits of the x coordinate
call getsprite ;point HL at the correct graphic
ld b,16 ;sprite is 16 lines high
drawloop: ;NEW
ld a,(hl) ;take a byte of graphic
ld (de),a ;and put it on the screen
inc hl ;next byte of graphic
inc e ;next column on screen
ld a,(hl) ;repeat for 3 bytes across
ld (de),a
inc hl
dec e
call nextlinedown
djnz drawloop ;repeat for all 16 lines
ret
;
Code: Select all
;
getsprite: ;don't worry much about how this works! for an alternative method that
;uses a table see 'getsprite_alternativemethod'
;Arrive A holding which pixel within a byte (0-7), point HL at correct graphic
ld h,0 ;we need to multiply A by 48, do it in HL
ld l,a
add hl,hl ;x2
add hl,hl ;x4
add hl,hl ;x8
add hl,hl ;x16
add hl,hl ;x32
ld bc,spritegraphic0
add hl,bc ;HL now pointing at correct sprite frame
ret
;
Code: Select all
org 32768 ;we can ORG (or assemble) this code anywhere really
;a beginner's, unoptimised sprite routine
main: halt ;this stops the program until the Spectrum is about to refresh the TV screen
;the HALT is important to avoid sprite flicker, and it slows down the program
call deletesprite ;we need to delete the old position of the sprite
call movesprite ;move the sprite! Could be based on player key input or baddy AI
call drawsprite ;get correct preshifted graphic, and draw it on the screen
jr main ;loop!
;
deletesprite: ;we need to delete the old sprite before we draw the new one. The sprite is 3 bytes wide & 16 pixels high
ld a,(x_coordinate) ;make C=xcor and B=ycor
ld c,a
ld a,(y_coordinate)
ld b,a
call yx2pix ;point DE at the corresponding screen address
ld b,16 ;sprite is 16 lines high
deleteloop: ;NEW!
ld a,0 ;empty A to delete
ld (de),a ;repeat a total of 3 times
inc e ;next column along
ld (de),a
dec e ;NEW
call nextlinedown ;move DE down one line
djnz deleteloop ;repeat 16 times
ret
;
movesprite: ;very simple routine that just increases the x coordinate
ld a,(x_coordinate)
inc a
ld (x_coordinate),a
cp 232 ;check if the sprite has moved all the way to the right (256-24)
ret c ;return if not
ld a,0 ;if yes then back to left
ld (x_coordinate),a
ret
;
drawsprite:
ld a,(x_coordinate) ;make C=xcor and B=ycor
ld c,a
ld a,(y_coordinate)
ld b,a
call yx2pix ;point DE at corresponding screen position
ld a,(x_coordinate) ;but we still need to find which preshifted sprite to draw
and 00000111b ;we have 8 preshifted graphics to choose from, cycled 0-7 in the right hand 3 bits of the x coordinate
call getsprite ;point HL at the correct graphic
ld b,16 ;sprite is 16 lines high
drawloop: ;NEW
ld a,(hl) ;take a byte of graphic
ld (de),a ;and put it on the screen
inc hl ;next byte of graphic
inc e ;next column on screen
ld a,(hl) ;repeat for 3 bytes across
ld (de),a
inc hl
dec e
call nextlinedown
djnz drawloop ;repeat for all 16 lines
ret
;
x_coordinate: db 0
y_coordinate: db 0
;
nextlinedown: ;don't worry about how this works yet!
inc d ;just arrive with DE in the display file
ld a,d ;and it gets moved down one line
and 7
ret nz
ld a,e
add a,32
ld e,a
ret c
ld a,d
sub 8
ld d,a
ret
;
yx2pix: ;don't worry about how this works yet! just arrive with arrive with B=y 0-192, C=x 0-255
ld a,b ;return with DE at corresponding place on the screen
rra
rra
rra
and 24
or 64
ld d,a
ld a,b
and 7
or d
ld d,a
ld a,b
rla
rla
and 224
ld e,a
ld a,c
rra
rra
rra
and 31
or e
ld e,a
ret
;
getsprite: ;don't worry much about how this works! for an alternative method that
;uses a table see 'getsprite_alternativemethod'
;Arrive A holding which pixel within a byte (0-7), point HL at correct graphic
ld h,0 ;we need to multiply A by 48, do it in HL
ld l,a
add hl,hl ;x2
add hl,hl ;x4
add hl,hl ;x8
add hl,hl ;x16
add hl,hl ;x32
ld bc,spritegraphic0
add hl,bc ;HL now pointing at correct sprite frame
ret
;
spritegraphic0: ;8 preshifted graphics, each one 3 bytes wide and 16 pixels high, this one a simple square
; ASM data file from a ZX-Paintbrush picture with 16 x 128 pixels (= 2 x 16 characters)
; block based output of pixel data - each block contains 16 x 16 pixels
; block at pixel position (0,0):
db 126, 0, 255, 0, 219, 0, 255, 0
db 189, 0, 195, 0, 255, 0, 126, 0
db 24, 0, 24, 0, 24, 0, 24, 0
db 24, 0, 36, 0, 66, 0, 129, 0
; block at pixel position (0,16):
db 63, 0, 127, 128, 109, 128, 127, 128
db 94, 128, 97, 128, 127, 128, 63, 0
db 12, 0, 12, 0, 12, 0, 12, 0
db 12, 0, 18, 0, 33, 0, 64, 128
; block at pixel position (0,32):
db 31, 128, 63, 192, 54, 192, 63, 192
db 47, 64, 48, 192, 63, 192, 31, 128
db 6, 0, 6, 0, 6, 0, 6, 0
db 6, 0, 9, 0, 16, 128, 32, 64
; block at pixel position (0,48):
db 15, 192, 31, 224, 27, 96, 31, 224
db 23, 160, 24, 96, 31, 224, 15, 192
db 3, 0, 3, 0, 3, 0, 3, 0
db 3, 0, 4, 128, 8, 64, 16, 32
; block at pixel position (0,64):
db 7, 224, 15, 240, 13, 176, 15, 240
db 11, 208, 12, 48, 15, 240, 7, 224
db 1, 128, 1, 128, 1, 128, 1, 128
db 1, 128, 2, 64, 4, 32, 8, 16
; block at pixel position (0,80):
db 3, 240, 7, 248, 6, 216, 7, 248
db 5, 232, 6, 24, 7, 248, 3, 240
db 0, 192, 0, 192, 0, 192, 0, 192
db 0, 192, 1, 32, 2, 16, 4, 8
; block at pixel position (0,96):
db 1, 248, 3, 252, 3, 108, 3, 252
db 2, 244, 3, 12, 3, 252, 1, 248
db 0, 96, 0, 96, 0, 96, 0, 96
db 0, 96, 0, 144, 1, 8, 2, 4
; block at pixel position (0,112):
db 0, 252, 1, 254, 1, 182, 1, 254
db 1, 122, 1, 134, 1, 254, 0, 252
db 0, 48, 0, 48, 0, 48, 0, 48
db 0, 48, 0, 72, 0, 132, 1, 2
;
Code: Select all
; This is a basic template file for writing 48K Spectrum code.
AppFilename equ "Rtape" ; What we're called (for file generation)
AppFirst equ $8000 ; First byte of code (uncontended memory)
zeusemulate "48K","ULA+" ; Set the model and enable ULA+
; Start planting code here. (When generating a tape file we start saving from here)
org AppFirst ; Start of application
;AppEntry halt
;we can ORG (or assemble) this code anywhere really
;a beginner's, unoptimised sprite routine
AppEntry : halt ;this stops the program until the Spectrum is about to refresh the TV screen
;the HALT is important to avoid sprite flicker, and it slows down the program
call deletesprite ;we need to delete the old position of the sprite
call movesprite ;move the sprite! Could be based on player key input or baddy AI
call drawsprite ;get correct preshifted graphic, and draw it on the screen
jr AppEntry ;loop!
;
deletesprite : ;we need to delete the old sprite before we draw the new one. The sprite is 3 bytes wide & 16 pixels high
ld a,(x_coordinate) ;make C=xcor and B=ycor
ld c,a
ld a,(y_coordinate)
ld b,a
call yx2pix ;point DE at the corresponding screen address
ld b,16 ;sprite is 16 lines high
deleteloop : ;NEW!
ld a,0 ;empty A to delete
ld (de),a ;repeat a total of 3 times
inc e ;next column along
ld (de),a
dec e ;NEW
call nextlinedown ;move DE down one line
djnz deleteloop ;repeat 16 times
ret
;
movesprite : ;very simple routine that just increases the x coordinate
ld a,(x_coordinate)
inc a
ld (x_coordinate),a
cp 232 ;check if the sprite has moved all the way to the right (256-24)
ret c ;return if not
ld a,0 ;if yes then back to left
ld (x_coordinate),a
ret
;
drawsprite :
ld a,(x_coordinate) ;make C=xcor and B=ycor
ld c,a
ld a,(y_coordinate)
ld b,a
call yx2pix ;point DE at corresponding screen position
ld a,(x_coordinate) ;but we still need to find which preshifted sprite to draw
and 00000111b ;we have 8 preshifted graphics to choose from, cycled 0-7 in the right hand 3 bits of the x coordinate
call getsprite ;point HL at the correct graphic
ld b,16 ;sprite is 16 lines high
drawloop : ;NEW
ld a,(hl) ;take a byte of graphic
ld (de),a ;and put it on the screen
inc hl ;next byte of graphic
inc e ;next column on screen
ld a,(hl) ;repeat for 3 bytes across
ld (de),a
inc hl
dec e
call nextlinedown
djnz drawloop ;repeat for all 16 lines
ret
;
x_coordinate : db 0
y_coordinate : db 0
;
nextlinedown : ;don't worry about how this works yet!
inc d ;just arrive with DE in the display file
ld a,d ;and it gets moved down one line
and 7
ret nz
ld a,e
add a,32
ld e,a
ret c
ld a,d
sub 8
ld d,a
ret
;
yx2pix : ;don't worry about how this works yet! just arrive with arrive with B=y 0-192, C=x 0-255
ld a,b ;return with DE at corresponding place on the screen
rra
rra
rra
and 24
or 64
ld d,a
ld a,b
and 7
or d
ld d,a
ld a,b
rla
rla
and 224
ld e,a
ld a,c
rra
rra
rra
and 31
or e
ld e,a
ret
;
getsprite : ;don't worry much about how this works! for an alternative method that
;uses a table see 'getsprite_alternativemethod'
;Arrive A holding which pixel within a byte (0-7), point HL at correct graphic
ld bc,spritegraphic0
ld hl,bc ;HL now pointing at correct sprite frame
ret
;
spritegraphic0 : ;8 preshifted graphics, each one 3 bytes wide and 16 pixels high, this one a simple square
; ASM data file from a ZX-Paintbrush picture with 16 x 128 pixels (= 2 x 16 characters)
; block based output of pixel data - each block contains 16 x 16 pixels
; block at pixel position (0,0):
db 126, 0, 255, 0, 219, 0, 255, 0
db 189, 0, 195, 0, 255, 0, 126, 0
db 24, 0, 24, 0, 24, 0, 24, 0
db 24, 0, 36, 0, 66, 0, 129, 0
; ; Replace these lines with your code
jp AppEntry ;
; Stop planting code after this. (When generating a tape file we save bytes below here)
AppLast equ *-1 ; The last used byte's address
; Generate some useful debugging commands
profile AppFirst,AppLast-AppFirst+1 ; Enable profiling for all the code
; Setup the emulation registers, so Zeus can emulate this code correctly
Zeus_PC equ AppEntry ; Tell the emulator where to start
Zeus_SP equ $FF40 ; Tell the emulator where to put the stack
; These generate some output files
; Generate a SZX file
output_szx AppFilename+".szx",$0000,AppEntry ; The szx file
; If we want a fancy loader we need to load a loading screen
; import_bin AppFilename+".scr",$4000 ; Load a loading screen
; Now, also generate a tzx file using the loader
output_tzx AppFilename+".tzx",AppFilename,"",AppFirst,AppLast-AppFirst,1,AppEntry ; A tzx file using the loader
Bonzer.
One block of graphic cannot move smoothly on the speccy—you need to store preshifted graphics.If I just have one block of graphics do you know why it's not moving smoothly? I just changed getsprite to point to the first set only.
Hi @myduis. The example in this thread is one of the simpler ways of doing sprites (LD on and remove with empty space). It would be fine for a game with no background like The Pyramid, or a game with PAPER platforms like Loony Zoo.myduis wrote: ↑Wed Apr 26, 2023 7:29 am The problem is that the code always "deletes" other elements of the screen when the sprite "passes" here - what needs to be done so that if I have a background element (or two sprites overlapping) they are not deleted but remain on the screen.
You need to somehow check new pixels whether they are set or not and when a new sprite movement is generated in this place, do not delete the already set background bits?
Code: Select all
;HL pointing at graphic
;DE pointing at screen
LD A,(DE) ;take a byte from the screen
XOR (HL) ;XOR the graphic onto it
LD (DE),A ;place the combination of background and graphic onto the screen
repeat...