Sprites that can be used from BASIC

developing/porting a new game or gaming framework? post in here!
User avatar
tricky
Posts: 7719
Joined: Tue Jun 21, 2011 9:25 am
Contact:

Re: any interest in a character display with sprites?

Post by tricky »

Warning, brain dump ahead!

To use or play with the sample, you only need the .ssd

OK, I spent two days trying to get BASIC to work, only to find that I had a bug in my assembler :oops:
This is the MODE 5, 8 sprites, 12 pixels wide, for a solid object moving horizontally, 9 pixels wide and how ever high you want.
This one has X% and Y% for bottom left corner of sprite on screen.
T% for top of sprite on sprite sheet and H% for height of sprite in pixels.
Setting height to 0 turns a sprite off.
?F%=-1 tells the system to render at the next end of screen (end of screen can be changed by setting ?V%=68, +/-1 move 4 pixels).
The game should wait until 0=?F% (for the sprite drawing to finish) before changing the sprite information, this avoids partial updates.
CALL C% clears the screen (from &6000 to &7FFF).
The BASIC sample *LOADs a screen shot and then calls PROCspr2scr(0,9) to copy the top 10 character rows to SPRite memory.
There is commented out code for saving the sprite memory, as this is what you actually need.
The idea of the screen (&6000-&7FFF) was to display what could be displayed in a sprite editor, which could be copied to and from sprite memory for loading, saving and using.
There is also FNtab, which is used like PRINT FNtab(0,0);"SCR2SPR"; to position the text cursor in the reduced screen size (16x256).
The sample is running at 25fps with the sprites taking 5/8ths of the frame to render.
I doubt that a basic game doing much could run at 50, but if you reduce it to 3 bouncing sprites, it will run at 50fps.
SPRITES loads at &52BB with its initialisation code starting at &5400, which is overwritten with sprite data which can go upto &5FFF and is followed by the screen display.
All the code in !BOOT could probably safely be moved into DEMO, but I'm not familiar with things like HIMEM.

I will probably put up an equivalent 8/5 pixel wide version with gfx data starting at &5800 later and maybe a better explanation of the sprite "page".
page.png
page.png (3.68 KiB) Viewed 5556 times
demo.png
demo.png (3.07 KiB) Viewed 5556 times
Main sprite routine:

Code: Select all

.draw_sprite ; Y=sprite
	ldx OLD_H,y : beq return : dex
	lda OLD_T,y : sta ld0+sm_lo : sta ld1+sm_lo : sta ld2+sm_lo
	lda OLD_X,y : and #3 : ORA #HI(SPRITES) : clc : sta ld0+sm_hi : adc #4 : sta ld1+sm_hi : adc #4 : sta ld2+sm_hi
	lda OLD_X,y : and #&7C : asl A : sta st0+sm_lo : sta st0+3+sm_lo : adc #8 : sta st1+sm_lo : sta st1+3+sm_lo : clc : adc #8 : sta st2+sm_lo : sta st2+3+sm_lo
	lda OLD_Y,y : lsr A : lsr A : lsr A : ora #HI(SCREEN) : sta st0+sm_hi : sta st0+3+sm_hi : sta st1+sm_hi : sta st1+3+sm_hi : sta st2+sm_hi : sta st2+3+sm_hi
	lda OLD_Y,y : and #7 : tay
.row
	.ld0 : lda SPRITES,x : .st0 : eor SCREEN,y : sta SCREEN,y
	.ld1 : lda SPRITES,x : .st1 : eor SCREEN,y : sta SCREEN,y
	.ld2 : lda SPRITES,x : .st2 : eor SCREEN,y : sta SCREEN,y
	dex : bmi done
	dey : bpl row
	lda st0+sm_hi : sec : sbc #1 : ora #&60 : sta st0+sm_hi : sta st0+3+sm_hi : sta st1+sm_hi : sta st1+3+sm_hi : sta st2+sm_hi : sta st2+3+sm_hi
	ldy #7 : bne row
.done : ldy #0
	RTS
Attachments
BasicSprites.zip
(6.49 KiB) Downloaded 126 times
Last edited by tricky on Thu Oct 08, 2020 9:57 pm, edited 1 time in total.
User avatar
tricky
Posts: 7719
Joined: Tue Jun 21, 2011 9:25 am
Contact:

Re: any interest in a character display with sprites?

Post by tricky »

So, the sprite sheet is indexed vertically by top and height and horizontally by the X coordinate AND 3.
The four bouncing sprites are at X MOD 3= 0, 1, 2 and 3.
The spaceship uses a similar technique to use the four offsets as for frames of animation.
The man and Pacman use the four frames at the different offsets as you would expect but also includes animation. The benefit of doing it this way is that you only need four copies for the four pixel offsets and to get animation. The downside is that if Pacman stops moving, it stops animating.
User avatar
sydney
Posts: 2925
Joined: Wed May 18, 2005 10:09 am
Location: Newcastle upon Tyne
Contact:

Re: any interest in a character display with sprites?

Post by sydney »

Hi Tricky.

I seem to be having a bit of trouble with this.
Is the following snippet of code using PROCscr2spr correctly?
If I do this then try to run the demo again using the newly produces SPRITES file it no longer works. It looks like only the sprite data has been saved but not the code as the screen no longer changes to the narrower width.
Alternatively I'm using it incorrcetly in which case could you explain it to me?
Cheers,
Simon

Code: Select all

CALL C% : REM CLS
*LOAD SAMPLE 6000
PROCscr2spr(0,9)
*SAVE SPRITES 5400 6000
CALL C% : REM CLS
END
User avatar
tricky
Posts: 7719
Joined: Tue Jun 21, 2011 9:25 am
Contact:

Re: any interest in a character display with sprites?

Post by tricky »

I think your code is correct, but it might need a control break to remove the sprites code as it is still running.
I should probably fix that!
I'll be able to have a look later and if I can't reproduce it, we can discuss it tonight if you are at abug.
User avatar
sydney
Posts: 2925
Joined: Wed May 18, 2005 10:09 am
Location: Newcastle upon Tyne
Contact:

Re: any interest in a character display with sprites?

Post by sydney »

I think I might have spotted my mistake after re-reading your post. I need to use a name other than SPRITES as this will overwite the code on the disk with the sprite data. I'll have another go and then get back to you.
User avatar
sydney
Posts: 2925
Joined: Wed May 18, 2005 10:09 am
Location: Newcastle upon Tyne
Contact:

Re: any interest in a character display with sprites?

Post by sydney »

Works like a charm!

Code: Select all

10 PRINT FNtab(0,0);"WRITE OR READ";
20 INPUT A$
30 IF A$="R" GOTO 100
40 CALL C% : REM CLS
50 *LOAD SAMPLE 6000
60 PROCscr2spr(0,9)
70 *SAVE SPRFILE 5400 6000 
80 CALL C% : REM CLS
90 END
100 REM READ 
110 CALL C% : REM CLS
120 *LOAD SPRFILE 
130 PROCspr2scr(0,9)
140 END
User avatar
tricky
Posts: 7719
Joined: Tue Jun 21, 2011 9:25 am
Contact:

Re: any interest in a character display with sprites?

Post by tricky »

Yep, that's what I just spotted too! I changed the name after testing them individually and then only saving and loading in one go (which works).
I was thinking of not using the CFS space for XYHT and instead putting then at the end of the code space.
And maybe adding a */SPR-OFF and */RESET.

I should probably get some better sample gfx as my man running is very poor if you don't know which leg is which and not great even if you do know!
User avatar
sydney
Posts: 2925
Joined: Wed May 18, 2005 10:09 am
Location: Newcastle upon Tyne
Contact:

Re: Sprites that can be used from BASIC

Post by sydney »

So the kids and I had a bit of a play with some graph paper and felt pens last night. We are going to write a Bruce Lee Vs Ninja's platform game using Trickys BASIC sprite routines.
IMG_20201009_094253.jpg
User avatar
tricky
Posts: 7719
Joined: Tue Jun 21, 2011 9:25 am
Contact:

Re: Sprites that can be used from BASIC

Post by tricky »

Unfortunately, MODE 5 pixels are twice as wide as they are high, unless those sprites are 32 pixels high.
I can easily do a mode 1 version, but it will be nearly twice the memory. could go for speccy 256x192 or 256 by anything really.
Or just very big sprites, if they fit. I guess I could also do a draw double height version for that chunky look.
User avatar
sydney
Posts: 2925
Joined: Wed May 18, 2005 10:09 am
Location: Newcastle upon Tyne
Contact:

Re: Sprites that can be used from BASIC

Post by sydney »

I had a feeling that would be a problem, so redesigning them is something to keep the kids busy with tonight! Or it could just be fat Bruce Lee Vs The Fat Ninjas!
I'm currently 1/4 of the way through writing a simple editor so maybe we'll have something to show by Sunday.
User avatar
tricky
Posts: 7719
Joined: Tue Jun 21, 2011 9:25 am
Contact:

Re: Sprites that can be used from BASIC

Post by tricky »

Like I said, MODE 1 is basically the same code, just nearly double the memory, or it could be giant Bruce Lee vs the giant ninjas, although I also like the fat Bruce Lee option for some reason.

I am looking forward to seeing how you get on with your editor.
User avatar
sydney
Posts: 2925
Joined: Wed May 18, 2005 10:09 am
Location: Newcastle upon Tyne
Contact:

Re: Sprites that can be used from BASIC

Post by sydney »

He's not as fat as you'd expect!
brucelee.png
brucelee.png (179 Bytes) Viewed 5391 times

Code: Select all

L.
    1 RESTORE 130
    2 C=0
   10 MODE 5 
   20 VDU 19,0,6;0;:REM COLOU 0 TO CYAN
   30 VDU 19,1,0;0;:REM COLOUR 1 TO BLACK
   35 VDU 19,2,3;0;:REM COLOUR 2 TO YELLOW
   40 VDU 19,3,7;0;:REM COLOUR 3 TO WHITE
   41 RESTORE 130
   60 FOR Y=15 TO 0 STEP-1
   64 FOR X=7TO 0 STEP -1
   70 READ COL
   80 GCOL 0,COL
   90 PLOT 69,X*8,Y*4     
   95 NEXT X
  100 NEXT Y
  130 DATA 0,0,1,1,0,0,0,0
  131 DATA 0,1,2,2,1,0,0,0
  132 DATA 0,1,2,1,2,0,0,0
  133DATA 0,1,2,2,2,0,0,0
  134DATA 0,0,2,2,0,0,0,0
  135DATA 0,2,2,2,2,0,0,0   
  136DATA 2,0,1,2,2,0,0,0
  137DATA 2,2,1,2,2,0,0,1
  138DATA 0,0,2,2,2,2,2,1
  139DATA 0,0,1,1,1,0,0,0
  140DATA 0,0,1,0,1,0,0,0
  141DATA 0,1,0,0,1,0,0,0
  142DATA 0,1,0,0,0,1,0,0
  143DATA 1,0,0,0,0,1,0,0
  144DATA 1,0,0,0,0,0,1,0
  145DATA 1,1,0,0,0,0,1,1
>
User avatar
tricky
Posts: 7719
Joined: Tue Jun 21, 2011 9:25 am
Contact:

Re: Sprites that can be used from BASIC

Post by tricky »

Looks fine, and he wasn't that tall anyway ;)
User avatar
sydney
Posts: 2925
Joined: Wed May 18, 2005 10:09 am
Location: Newcastle upon Tyne
Contact:

Re: Sprites that can be used from BASIC

Post by sydney »

tricky wrote: Fri Oct 09, 2020 2:17 pm I am looking forward to seeing how you get on with your editor.
So I'm doing pretty well but if I were to write another one, which I might, I think I'd do it very differently.

How it will work:
The sprite file is displayed on the right of the screen.
The left of the screen houses a zoomed in area in which you can edit pixels.
Editing is done by moving a cursor around the zoomed area and setting a pixel colour by pressing 0-3.
When the edge of the zoomed area is reached it will scroll 4 pixels horizontally or 8 pixels vertically.
The pallette can be changed by pressing 0-3 to select a colour followed by 0-7 to assingn that colour.

What I've got done:
Loading sprites
Saving sprites
Displaying sprite file
Zoomed editing area
Pallette changing

Left to do:
Display cursor
Move cursor
Set pixel colour
Scroll zoomed area
mainmenu.png
editscreen.png
palette.png
User avatar
sydney
Posts: 2925
Joined: Wed May 18, 2005 10:09 am
Location: Newcastle upon Tyne
Contact:

Re: Sprites that can be used from BASIC

Post by sydney »

It's going to be pretty slow at moving the zoomed window the way I've done it so I might need to write it in assembler to make it useable.
User avatar
tricky
Posts: 7719
Joined: Tue Jun 21, 2011 9:25 am
Contact:

Re: Sprites that can be used from BASIC

Post by tricky »

Assuming the left is zoomed in 4X, you could use the hardware cursor for the left hand side, although it might be a little clumsy from BASIC.
IIRC, you can set the width in bytes, the start and end in scan lines and the address in characters (real addr / 8). Or you could use a sprite ;)
I was also thinking that you could preview the animation somewhere.
User avatar
Andrew_Waite
Posts: 312
Joined: Tue Aug 30, 2016 3:58 pm
Contact:

Re: Sprites that can be used from BASIC

Post by Andrew_Waite »

sydney wrote: Fri Oct 09, 2020 9:46 am So the kids and I had a bit of a play with some graph paper and felt pens last night. We are going to write a Bruce Lee Vs Ninja's platform game using Trickys BASIC sprite routines.IMG_20201009_094253.jpg
I use Excel's Conditional Formatting function to design sprites.

BBC BASIC code can even be written in Excel, then copy and pasted directly into BeebEm.
Attachments
Excel Sprite a.png
User avatar
sydney
Posts: 2925
Joined: Wed May 18, 2005 10:09 am
Location: Newcastle upon Tyne
Contact:

Re: Sprites that can be used from BASIC

Post by sydney »

tricky wrote: Sat Oct 10, 2020 3:35 pm Assuming the left is zoomed in 4X, you could use the hardware cursor for the left hand side, although it might be a little clumsy from BASIC.
IIRC, you can set the width in bytes, the start and end in scan lines and the address in characters (real addr / 8). Or you could use a sprite ;)
I was also thinking that you could preview the animation somewhere.
The assembler shouldn't be difficult, it's only moving 128 bytes up 8 places in memory per char line x 16 char lines. I'd be finshed it now if the wife wasn't wanting me to do stuff round the house.
Andrew_Waite wrote: Sat Oct 10, 2020 3:43 pm
sydney wrote: Fri Oct 09, 2020 9:46 am So the kids and I had a bit of a play with some graph paper and felt pens last night. We are going to write a Bruce Lee Vs Ninja's platform game using Trickys BASIC sprite routines.IMG_20201009_094253.jpg
I use Excel's Conditional Formatting function to design sprites.

BBC BASIC code can even be written in Excel, then copy and pasted directly into BeebEm.
That's a good way to do it. I used that technique for my Beebout game - mainly to design the levels rather than the sprites (all 2 of them!). ISTR it produced a pile of DATA statements that I simply pasted into notepad++.

I'm writing a sprite editor in the hopes that people new to programming will be able to use it along with Tricky's sprite routines to write a simple game and maybe catch the bug and write more or learn assembler etc. Once I've got this working I'm going to write a simple game and hopefully write some kind of tutorial on how to use all this to write games.
I find a lot of people who express an interest in writing games are put off by having to use external programs to do it. Once you start talking about assemblers, compilers, text editors etc people lose interest. Many people (me included) want to use their beebs, be that for playing games or writing them.
User avatar
sydney
Posts: 2925
Joined: Wed May 18, 2005 10:09 am
Location: Newcastle upon Tyne
Contact:

Re: Sprites that can be used from BASIC

Post by sydney »

So I'm trying to write an assembler routine which will scroll the zoomed window by copying bytes in memory

Code: Select all

|6000|6008|6010| : 6008 -> 6000 : 6010 -> 6008 etc across the screen for 32 chars(&80 bytes)
|6001|6009|6011| : 6009 -> 6001 : 6011 -> 6009 
|6002|600A|6012| : 600A -> 6002 : 6012 -> 600A
|6003|600B|6013| : 600B -> 6003 : 6013 -> 600B
|6004|600C|6014| : 600C -> 6004 : 6014 -> 600C
|6005|600D|6015| : 600D -> 6005 : 6015 -> 600D
|6006|600E|6016| : 600E -> 6006 : 6016 -> 600E
|6007|600F|6017| : 600F -> 6007 : 6017 -> 600F
My code seems to work on a standard MODE 5 screen:

Code: Select all

   10MODE 5
   20*LOAD HSCROLL
   30 FOR I =0 TO 20
   40 PRINT"123456789012345"
   60NEXT I
   64 FOR I = 1 TO 10
   65 ?&5298=&08
   66 ?&5299=&60
   67 ?&529B=&00
   68 ?&529C=&60
   70 CALL &5290
   71 NEXT
Gives the following output:
test1.png
But when I try to CALL &5290 from my sprite editor I get a ' at line XXXX' error:

Code: Select all

DEFPROCright
   IF CURSORX%<15 THEN CURSORX%=CURSORX%+1
   PROChscroll(&60,&08,&60,&00)
ENDPROC

...

DEFPROChscroll(SHI%,SLO%,DHI%,DLO%)
	?&5299= SHI%
	?&5298= SLO%
	
	?&529C= DHI%
	?&529B= DLO%
	
	CALL &5290
ENDPROC


Here is the offending routine.

Code: Select all

   10P%=&5290
   20[
   30 PHA
   40 TYA
   50 PHA
   60 TXA
   70 PHA
   80 .start LDX #&00
   90       .s1 LDA &6008,X 
  100       .d1 STA &6000,X 
  110       INX
  120       CPX #&80  
  130    BNE s1
  140    INC s1+2
  150    INC d1+2
  160    LDA s1+2
  170    CMP #&70
  180 BNE start
  190 PLA
  200 TAX
  210 PLA
  220 TAY
  230 PLA
  240 RTS
  250 .end
  260 ]
  270 PRINT end-start
I've never used assembler from BASIC before so I could just be doing it wrong. I added the code to save the registers to the stack incase that was the problem but it made no difference. I ran the program and saved the code with

Code: Select all

*SAVE HSCROLL 5290 52B5
For the sake of completeness here is the assembler output:

Code: Select all

5290          
5290 48       PHA
5291 98       TYA
5292 48       PHA
5293 8A       TXA
5294 48       PHA
5295 A2 00    .start LDX #&00
5297 BD 08 60 .s1 LDA &6008,X 
529A 9D 00 60 .d1 STA &6000,X 
529D E8       INX
529E E0 80    CPX #&80  
52A0 D0 F5    BNE s1
52A2 EE 99 52 INC s1+2
52A5 EE 9C 52 INC d1+2
52A8 AD 99 52 LDA s1+2
52AB C9 70    CMP #&70
52AD D0 E6    BNE start
52AF 68       PLA
52B0 AA       TAX
52B1 68       PLA
52B2 A8       TAY
52B3 68       PLA
52B4 60       RTS
52B5          .end
        32
User avatar
tricky
Posts: 7719
Joined: Tue Jun 21, 2011 9:25 am
Contact:

Re: Sprites that can be used from BASIC

Post by tricky »

I can't see anything wrong with the code, but might BASIC be overwriting that are of memory?
I dropped HIMEM in my code to leave a gap above it for my code, but a better way is to DIM some space and use that.
You can also inx : bpl (Branch on PLus) instead of CPX #&80 : bne.
User avatar
sydney
Posts: 2925
Joined: Wed May 18, 2005 10:09 am
Location: Newcastle upon Tyne
Contact:

Re: Sprites that can be used from BASIC

Post by sydney »

AAAAAARRRRRRRRGGGGGGGHHHHHHHHH!
I changed HIMEM but didn't save!
It works beautifully! Next is to get the right scroll and vertical scrolls done and I'll be nearly finished.
It'll be Wednesday before I get a chance to look at this again unfortunately.
User avatar
streaks
Posts: 279
Joined: Thu Oct 13, 2005 3:08 pm
Contact:

Re: Sprites that can be used from BASIC

Post by streaks »

I'd have killed for this :O
streaksy (at) gmail (dot) com
User avatar
tricky
Posts: 7719
Joined: Tue Jun 21, 2011 9:25 am
Contact:

Re: Sprites that can be used from BASIC

Post by tricky »

It's never too late, or so they say ;)
Sorry if you mean be cause you've done your own :)
User avatar
tricky
Posts: 7719
Joined: Tue Jun 21, 2011 9:25 am
Contact:

Re: any interest in a character display with sprites?

Post by tricky »

tricky wrote: Wed Oct 07, 2020 6:17 pm ...
Main sprite routine:

Code: Select all

.draw_sprite ; Y=sprite
	ldx OLD_H,y : beq return : dex
	lda OLD_T,y : sta ld0+sm_lo : sta ld1+sm_lo : sta ld2+sm_lo
	lda OLD_X,y : and #3 : ORA #HI(SPRITES) : clc : sta ld0+sm_hi : adc #4 : sta ld1+sm_hi : adc #4 : sta ld2+sm_hi
	lda OLD_X,y : and #&7C : asl A : sta st0+sm_lo : sta st0+3+sm_lo : adc #8 : sta st1+sm_lo : sta st1+3+sm_lo : clc : adc #8 : sta st2+sm_lo : sta st2+3+sm_lo
	lda OLD_Y,y : lsr A : lsr A : lsr A : ora #HI(SCREEN) : sta st0+sm_hi : sta st0+3+sm_hi : sta st1+sm_hi : sta st1+3+sm_hi : sta st2+sm_hi : sta st2+3+sm_hi
	lda OLD_Y,y : and #7 : tay
.row
	.ld0 : lda SPRITES,x : .st0 : eor SCREEN,y : sta SCREEN,y
	.ld1 : lda SPRITES,x : .st1 : eor SCREEN,y : sta SCREEN,y
	.ld2 : lda SPRITES,x : .st2 : eor SCREEN,y : sta SCREEN,y
	dex : bmi done
	dey : bpl row
	lda st0+sm_hi : sec : sbc #1 : ora #&60 : sta st0+sm_hi : sta st0+3+sm_hi : sta st1+sm_hi : sta st1+3+sm_hi : sta st2+sm_hi : sta st2+3+sm_hi
	ldy #7 : bne row
.done : ldy #0
	RTS
The subject of sprite plotting comes up quite often on *., so I thought a bit more elaboration might be useful here.

Assuming that we have a sprite X and Y in pixels, although many other representations are useful, we first need to calculate a screen address.
I'm assuming here that the screen is MODE 1 and has been shrunk horizontally to 256 pixels which is two pages &200. I might look at full size later on.

The way that the screen (assuming SCREEN is say &4000) is layed out, we need:
A vertical offset within a character: lda spr_y : and #7
A character row address: lda spr_y : and #&F8 : lsr A : lsr A : adc #HI(SCREEN) ; if screen is &40000, we could use ora, but it is the same cost here
A row offset in bytes: lda spr_x : and #&FC : asl A ; A=LO(offset), C=HI(offset)
And probably an offset within a byte: lda spr_x : and #3 ; used to choose which pixel offset copy of the sprite to use.
Which sprite to draw: lda spr_z
You might also want animation and this could be combined with pixel offset as in the example above.
If we are drawing a sprite that can be at various pixel offsets within a byte and it is 4 pixels wide, we will need to draw 2 columns (8 pixels wide) for the three offsets that aren't 0. We can either optimise for the aligned case and draw 1, and 2 for the rest, or keep it simple and make the sprite 5 pixels wide ;)

We also need to know how we want to draw the sprite and here I' assuming that the sprites are accessed with indexed addressing and the screen with indirect addressing:
We might need to clear the sprite:
lda #0 : sta (screen0),y : sta (screen1),y
Just copy it to the screen, this is the fastest and most simple, but will erase any overlapping graphics.*
lda spr_col0,x : sta (screen0),y : lda spr_col1,x : sta (screen1),y
ORing sprites on gets around the issue of sprites erasing overlapping graphics, but works best with a single colour 3 sprite like my Pac-Man.
lda spr_col0,x : ora (screen0),y : sta (screen0),y : lda spr_col1,x : ora (screen1),y : sta (screen1),y
EOR (or XOR) this is useful as drawing it a second time puts everything back as it was.**
lda spr_col0,x : eor (screen0),y : sta (screen0),y : lda spr_col1,x : eor (screen1),y : sta (screen1),y
Masked drawing uses a second set of graphics to keep or discard the background pixels which looks much neater.
lda spr_msk0,x : and (screen0),y : ora spr_col0,x : sta (screen0),y : lda spr_msk1,x : and (screen1),y : ora spr_col1,x : sta (screen1),y
If you have a lot of sprite data, which is doubled with masks, you can "auto mask" which needs an extra 256 byte page aligned table of "keep" bits. ***
lda spr_col0,x : sta mask0+1 : .mask0 : lda masks : and (screen0),y : ora spr_col0,x : sta (screen0),y
lda spr_col1,x : sta mask1+1 : .mask1 : lda masks : and (screen1),y : ora spr_col1,x : sta (screen1),y
A similar technique can be used to draw mirrored (1 table) sprites and to create the misaligned 3 pixel offsets on the fly (6 tables) and even mirrored and shifted together (6 tables). This is what I do in my Donkey Kong demo as it would otherwise require about 100K of graphics data.

* If a black pixel is left around the sprite and it only moves a single pixel, then the copy method can erase as it goes. My Frogger port uses this technique for everything except the frog.
** if two identical sprites are on top of each other, they disappear.
*** sprite masking can be done much more efficiently on the 6502 (B/B+) than the 65C02 (Master/Compact) by using "undocumented opcodes".

Whether to use indexed addressing or indirect indexed addressing is usually a matter of which will be faster, but the indexed version usually requires self modifying code, so isn't suitable for code in ROM but is fine for code in RAM and data in ROM. Generally self modifying code ,x is slower to update and indirect indexed addressing (),y is slower to execute. If something is written once and then read a few times, it is probably quicker to use ,x but if the address is reused (EOR) then it can be faster to use (),y.

For this EOR (XOR) routine, we use the sprite address once and the screen address twice, so will go with ,x for sprite access and (),y for screen address. The sprite data is assumed to start at &3000 and be stored in columns with the four horizontal offsets for the first column before the second column. We are also assuming that the y coordinate of the sprite is for the bottom pixel, this allows a small optimisation in the inner loop.

Code: Select all

lda spr_y : and #7 : tay                        ; offset within char row
eor spr_y : lsr A : lsr A : ora #&40 : tax      ; eor saves lda,and
tax : lda spr_x : and #&FC : asl A
{ bcc lhs : inx : clc : .lhs } stx screen0+1 : sta screen0
adc #8 { bcc lhs : inx : .lhs } stx screen0+1 : sta screen0
lda spr_x : and #3 : ora #&30 : sta sprite0+2   ; pixel offset indexes column/page
ora #4 : sta sprite1+2                          ; second column is four pages after the first column.
lda spr_bottom : sta sprite0+1 : sta sprite1+1  ; sprite low byte starting at bottom
ldx spr_height_minus_one
This looks like a lot of setup code and it is, which is why sometimes it can be better to store the data in a way that is more friendly to drawing as well as pixel X,Y which is more friendly to your brain and collision detection.

To draw these arbitrarily high sprites that we are going to assume don't go off any edge of the screen with EOR drawing we could do:

Code: Select all

.draw
	.sprite0 : lda sprites,x : eor (screen0),y : sta (screen0),y
	.sprite1 : lda sprites,x : eor (screen1),y : sta (screen1),y
	dex : bmi done       ; will only be taken once, so optimise for not taking branch
	dey : bpl draw       ; drawing up allows us to dey : bpl instead of inc : cmp #8 : bne
	dec screen0+ 1 : dec screen0+ 1 : dec screen1+ 1 : dec screen1+ 1    ; several ways to do this
	ldy #7 : bne draw    ; ALWAYS, save a byte over jmp, but still takes 3 cycles
.done
	RTS
The code in the loops could be "unrolled" but as we are using self modifying code and would cost more to write 8 copies of the addresses.

Every time I write one of these routines, which is several times per game, they are slightly different and I have just written this in the browser as I am not on my dev machine, so this is probably not quite the same as any of my previous routines ;) AND IS UNTESTED!


The last thing to note is that adding more columns, making the sprite wider, is less than a linear cost increase as the looping cost doesn't change and calculating the additional addresses is much cheaper than the first. The only cost that is linear is moving to the next char row.

I do have versions of this code where I know I have 9 pixel high sprites, so unroll the loop twice and only include the bmi after the dex in the second loop. or even 17 pixel high ones that are unrolled three times. There is a 16 pixel high one unrolled three times that checks x<0 between the second and this copies and in the third copy. Usually there isn't room for many versions of this code: drawing types, widths and many unrolled copies but in Frogger that is exactly what there is as there is so much sprite area to draw.
One thing that I do in a couple of my games including Frogger is:

Code: Select all

.loop
lda sprite_row0,y : sta screen0_row0,y : sta screen1_row0,y : sta screen2_row0,y   ; top half
lda sprite_row1,y : sta screen0_row1,y : sta screen1_row1,y : sta screen2_row1,y   ; bottom half
dey : bpl loop
RTS
This only works as the sprites are always character row aligned and self erase.
In the actual these routines are optimised for 12 and 14 pixel high sprites and probably other sizes which require the loops to be unrolled and the sprite read ,x while the screen is written ,y as for say a 12 pixel high sprite, Y must count 6 and skip 2 (top_row+2,y for the top and bottom_row,y for the bottom) but there isn't room to have gaps in the sprite data.

Enough for now, please feel free to point out any mistakes or improvements and ask any questions as I'm sure you won't be the only one asking "why do it like that?", to which the answer will probably be "because I couldn't think of a better way ;)".
Last edited by tricky on Sun Jun 26, 2022 5:51 pm, edited 1 time in total.
User avatar
fizgog
Posts: 618
Joined: Thu Jun 17, 2021 3:18 pm
Location: Nottinghamshire
Contact:

Re: Sprites that can be used from BASIC

Post by fizgog »

Nice write up on how you do sprite plotting for your games =D>

We need more guides and explanations like this to encourage new developers to the beeb
Pitfall, Gridrunner, Matrix: Gridrunner 2, LaserZone, AcornViewer, AcornPad
gfoot
Posts: 987
Joined: Tue Apr 14, 2020 9:05 pm
Contact:

Re: Sprites that can be used from BASIC

Post by gfoot »

Yes, thanks for going through all of that tricky, both the code and the explanation, there are some great tips and optimisations in there! The lookup tables for flipping and masks and the way you're using separate X and Y indices to get cheap vertical offsets (if I understood it correctly...) were new to me, as well as the huge benefit you're getting from using a square screen, being able to assume that all addresses in each line have the same high byte...
User avatar
tricky
Posts: 7719
Joined: Tue Jun 21, 2011 9:25 am
Contact:

Re: Sprites that can be used from BASIC

Post by tricky »

Mode 0-3 are &200 per line and 4-6 &100 per line with the reduced width screen.

I mostly do it because I mainly port arcade games that were originally in 256x224 or 224x256 but the speed boost and memory saving are good too :)

RichTW has a very optimised sprite routine in his demo Blurp! as does Sarah in her game White Light.

I think we all benefit when we look at this sort of thing together. There was some discussion on optimal sprite routines when I was writing Frogger.

My R-Type type demo uses a few more sprite routines that are tuned for special situations and the idea is to take that into account when building a game.
gfoot
Posts: 987
Joined: Tue Apr 14, 2020 9:05 pm
Contact:

Re: Sprites that can be used from BASIC

Post by gfoot »

Yes, there seem to be a lot of trade-offs to balance, and no doubt finding the right balance for the particular game is important.

I've started working on a kind of platform game, but not implemented proper sprite drawing yet - I'm just drawing solid boxes for now. For this game the player movement mechanics are really important and I'm experimenting with those first. I need to determine what frame rate I'm aiming for, and whether I need to support unaligned sprites to get the gameplay I want - especially the character's movement speed. I also need to determine whether I'm competent enough to actually get the gameplay feeling how I want it to anyway, before investing too much into anything else! Then I figured I can write sprite routines that are only as complex as I need them to be.

Other decisions like whether to support scrolling may also affect sprite requirements, I think, as if you want to scroll to keep the player right in the centre, sub-byte movement isn't going to work well (like in the JCB game!) Equally, scrolling means you can zoom in a bit, and get away with byte-aligned movement more easily, at the cost of needing to be able to draw enough background each frame to keep up with the scrolling. Another trade-off! Repton 3 runs much slower when the player is moving (so the screen is scrolling) than when the player is stationary.

At the moment I'm running in MODE 1 at 25Hz with no scrolling, and byte-aligned movement, and it takes about four seconds to cross the screen horizontally. I think that's a bit fast in the context of my game, so I probably will need to implement some form of sub-byte movement and sprite drawing. Similarly for vertical movement, I'm currently moving a whole line at a time, which worked well enough in games like Repton and Codename Droid, and even my platformer jump mechanics feel quite good in spite of this - but I think overall movement speed is going to be the thing that means I need to break away from aligning everything.
User avatar
tricky
Posts: 7719
Joined: Tue Jun 21, 2011 9:25 am
Contact:

Re: Sprites that can be used from BASIC

Post by tricky »

Yep, movement speed can certainly make or break a game, much more than framerate.
My Really-X demo uses hardware scrolling to go two mode 1 pixels, but doesn't work on CRTs although I think all the emulators and rgb2hdmi support it.
My Scramble does two pixel software scrolling, but only right to left (this could be 1 pixel but would need more graphics and the original is 2 pixels).
My R-Type tech demo does 1 pixel mode 1 hardware scrolling using two copies of the screen and the 2 pixel hardware scrolling trick.

Maybe we need another thread on scrolling.

Anyway, for your game, if you get the game going, changing sprite routines isn't the end of the world as long as you have enough memory for the data. One way is to make the animation into the horizontal movement and only have ladders on character boundaries.
User avatar
TobyLobster
Posts: 622
Joined: Sat Aug 31, 2019 7:58 am
Contact:

Re: Sprites that can be used from BASIC

Post by TobyLobster »

Great stuff. Putting code in zero page can also help with speed, if you have self modifying code. Although it does tend to use a lot of valuable zero page space! e.g. for one of tricky's nice examples for a drawing sprite with a mask:

Code: Select all

    lda spr_col0,x
    sta mask0+1
.mask0
    lda masks
    and (screen0),y
    ora spr_col0,x
    sta (screen0),y
If I've got my cycle counting right: putting this in zero page we will save one cycle for the 'sta mask0+1' instruction (three cycles instead of four), and can similarly gain another by replacing 'ora spr_col0,x' with 'ora mask0+1'.
Post Reply

Return to “new projects in development: games”