Language…
9 users online: Batata Douce, DanMario24YT, ForthRightMC, Isikoro, kurtistrydiz, mathew, MegacesarCG, monkey03297, TheOrangeToad - Guests: 290 - Bots: 375
Users: 64,795 (2,378 active)
Latest user: mathew

Dynamic sprites and you!

Dynamic Sprites and You!




Hey there everyone! This is a dynamic sprite tutorial, which I’m sure you know since you clicked on the link to get in here. So yeah, here we go into the exciting world of Dynamic Sprites!

What you need:

ASM experience
Spriting experience
Patience (found at your nearest virtue retailer)

What is a Dynamic Sprite?
There seems to be quite a stigma surrounding dynamic sprites, and most of that seems to stem from people having no idea what I dynamic sprite really is. So what is it? It’s just a sprite with an over glorified graphics routine. More specifically, it allows dynamic uploads to VRAM, allowing you basically as many frames in a sprite as you like.

What does this tutorial cover?
As the only difference between a dynamic sprite and a regular sprite is the graphics routine, I will only be covering the graphics routine. And, beyond that, I will only be covering the differences between a regular one and a dynamic one. This is why you need some spriting experience, or at least have a basic idea of how to sprite, because I won’t be explaining anything that is done in a normal sprite because that would get repetitive.

SO HOW DO I FREAKING MAKE ONE ALREADY!?
Well fine, settle down, just needed to get the semantics and warnings out of the way. Here’s the real tutorial:

DSX.asm:
The first thing you want to do is apply DSX.asm, available in the patches section. If you don’t, you are going to ask someone for help, they are going to get confused for about 20 minutes trying to figure out what is wrong with the code, and then you will finally remember that you hadn’t applied it yet, then you’ll feel real embarrassed. Yep.

GFX:
The most important part of dynamic sprites. Basically you want it laid out so that each frame takes up 1 32x32 square. You can also do less, but I’m sure you can figure that out for your self. In case you are too lazy to make your own GFX, here is a gfx file that works, and it is the one I will be using in my demonstration.

New library routines:
This is the hard part of dynamic spriting, but, luckily, it’s already been done for us. Thank smkdan and edit1754 for getting this out of the way for us, now all we need to do is copy and paste them. The new routines can be found here if you are assembling with xkas, or here if you are assembling with TRASM. (You are still going to want all the same library routines as you would have in a normal sprite). What you want to do is copy all of that code into your sprite and NEVER TOUCH IT AGAIN!

TO THE FREAKING CODE ALREADY!

Alright, here goes. A dynamic GFX routine starts out just like a regular one. You need tilemaps, offsets, etc. The difference mainly comes in the fact that you tile numbers will ALWAYS be the same, no matter what frame you are on or where you get your GFX from in the original file, they will always look like this:
Code
Tiles:
db $00,$02,$20,$22

(Obviously replace db with dcb if you are using TRASM)
00 is the top left of the dynamic sprite upload, 02 the top right, etc. If you want to grab less tiles just change this accordingly.
Offset tables are essentially the same as in a normal sprite, just set those up however you like. Telling it where to grab from is where it gets a bit weird. Instead of everything being 2 apart, it’s only 1 apart, since everything is apparently set up in 32x32 tiles. So the top left of the file is $00, 32 pixels to the right is $01, down 32 pixels is $11, etc., so a 16 frame sprite (like the one in the file I linked) would have a table like so:

Code
 Frames:
db $00,$01,$02,$03
db $10,$11,$12,$13
db $20,$21,$22,$23
db $30,$31,$32,$33

(Once again, that’s Xkas, adjust accordingly for TRASM)


So, now that we’ve got the pesky tables out of the way, time to start writing the routine. It starts just like any other routine, as I’ve said before.
Code
 SUB_GFX:            JSR GET_DRAW_INFO
LDA $157C,x             ; $02 = sprite direction
STA !TempDir		;Pretty simple, just like you would normally set a sprites direction.
STZ !TilesTemp		;Used to keep track of the number of tiles drawn
LDA !FrameNum,x		;You can also load a static value to here,
                        ;this is used as an index for the frames 
                        ;table. This is freeRAM
AND #$0F
PHX				;\Get the place to grab from into the accumulator
TAX				;|
LDA Frames,x			;|
PLX				;/

So that’s all pretty simple I would suppose, this is where it really starts being different:
Code
 JSR GETSLOT	;This is that part we should thank smkdan for
BEQ EndIt			;Don’t draw if there is no empty dynamic sprite slot
STA !OneTileTemp		;Store the tile into scratch RAM, I use $07

And then we get into a normal sprite writing loop, just like in any other sprite, I’ll comment on what’s important:
Code
PHX
LDX #$00
TileLoop:
LDA $00
PHY
LDY !TempDir
BEQ right
CLC
ADC XOff,x
BRA Flipped
right:
SEC
SBC XOff,x
Flipped:
PLY
STA $0300,y
LDA $01
PHY
LDY !TempDir
BEQ down
CLC
ADC YOff,x
BRA FlippedY
down:
SEC
SBC Yoff,x
FlippedY:
STA $0301,y
LDA !OneTileTemp		;remember the tile we stored earlier? 
                                ;Now we get it back and store it 
                                ;like we would a normal tile, except
                                ;we add the value from the Tiles table to it.
CLC
ADC Tiles,x
STA $0302,y
PHX
LDX $15E9			;Get sprite index back
LDA $15F6,x			;You know the drill here
ORA $64
LDX !TempDir
BNE left
ORA #$C0
left:
PLX
STA $0303,y
INC !TilesTemp
INY
INY
INY
INY
INX
CPX #$04		;Tells it to draw 4 tiles, which is the max
                        ;allowed. Less is fine though, 
                        ;just set up your tables accordingly.
BCC TileLoop
PLX
LDA !TilesTemp	;The number of tiles drawn
BEQ EndIt		;Don’t draw if we didn’t get any
DEC			;\Yada, yada, normal drawing routine
LDY #$02		;|16x16 tiles
JSL $01B7B3		;/
EndIt:
RTS

Notice how the only difference really (Besides my few quirks I have going on), is that you load the tile that it finds for you? Yeah, that’s really it, it is that simple. Everything hard has been done for us. The only thing left for you to do is feed a value into !FrameNum and jump to the routine like normal. You can change it however you like it to be once you understand it.

To use this graphics routine:
The easiest thing to do is to make the sucker spin. To do that it’s just some simple code:

Code
INC !FrameNum,x
LDA !FrameNum,x
AND #$10		;Check if the 4th (from right, 0 indexed) is set
LSR			;If it is, flip it, if not, don’t flip it, simple as that
LSR			;
LSR			;
LSR			;
STA $157C,x		;Store to the direction RAM
LDA !FrameNum,x	;\Only allow 16 frames
AND #$1F		;|
STA !FrameNum,x	;/


Oh and one more important thing, at the very bottom of the file you need:
Code
GFXPointer:
dw SPRITEGFX
SPRITEGFX:
INCBIN sprites\barrel.bin		;included graphics file

The first two lines are unnecessary if you are using TRASM, and replace barrel.bin with the name of the gfx file you are using.

And the final product would be a little something like this (once again, it uses xkas, should be quite similar with TRASM though).

Oh, and all dynamic sprites need to use sp4, so set that up in the cgf.

And one more thing: Sprite graphics that take up more than 1 bank in ROM (over 64000 bytes), it takes a different dynamic sprite routine which you can get here, then follow these instructions for manual GFX insertion:


Originally posted by edit1754
Steps to inserting GFX:

1. BACK UP YOUR ROM!!! You will always want an up-to-date copy if you corrupt it accidentally

2. Open your SMW ROM in translhextion

3. Keep scrolling down until you find a large blank area full of zeros.

4. Look for an address that ends in "0200" or "8200" (these are beginnings of ROM banks) ...or you can overwrite the old blowhard GFX. Also, make sure there are at least 8 zeros before that, for the RATS tag. (unless the space is used by the old RATS tag)

5. To make sure there is enough space, use the select block feature to select an area that is 0x7F80 (32640) bytes long. To do this, go to Selection > Select Block, click "number of bytes to select", and type the decimal value of the space required. (in this case, it's 40960, which is 1 and 1/4 ROM banks)

6. If there are only zeros in the space, go to the next step, otherwise repeat from step 3 to find another space.

7. Go back to the address at the start and write it down (the PC offset)

8. Open "blowhardgfx.bin" in another translhextion window and copy/paste the data into the starting address in the SMW ROM's blank space you found. When the paste dialog box comes up, make sure you select "overwrite" and NOT "insert".

9. go back 8 bytes before your starting address and type 53 54 41 52 00 A0 FF 5F for your RATS tag

10. Open Lunar Address and select "LoRom - PC". If it doesn't automatically say PC and SNES under the text boxes at the bottom of the window, clear out one or both until it shows the labels so you know which is which.

11. Type the first address you wrote down into Lunar Address where it says PC and write down the address it says in "SNES".

12. Open blowhard.asm in notepad and search for "GFXADDR", it should be near the top.

When you find it, you will see something like this:
GFXADDR = $XXXXXX

13. Go back to the last address you wrote down (the converted address), it should look something like this: $12:3456.

14. For the converted address, enter it in like this:
GFXADDR = $123456

15. save the ASM file and insert the sprite (yiblowhardv2.asm, yiblowhardv2.cfg) like you would any sprite.

(Obviously create your own GFXADDR at the beginning of your sprite if you don't have one)

And that should basically be your first dynamic sprite right there. You have fun now.
Pretty cool document you have there, HuFlungDu. Dynamic sprites really aren't as hard as people put on like they are.

I'm sure this can teach someone interested in programming dynamic sprites the basics.

Give Mario some love?~
Originally posted by zKiP
Pretty cool document you have there, HuFlungDu. Dynamic sprites really aren't as hard as people put on like they are.

Yes yes, when all the routines are already made for you so only copy-pasta is necessary. Try coding one from scratch otherwise. β)

EDIT: If it's no worry, do you think you could do something about the table stretch?

World Community Grid: Thread | Team
 
Now this seems like it could be very useful. Thank you for posting it. so totally creating that spinning muncher now

By the way, wasn't there a tool somewhere that could take graphics from a .bin file and rotate them? Because that would make creating the graphics for one of these quite a bit easier.

Also what table stretch?
My YouTube channel
Get the official ASMT resource pack here!

I think you mean this tool by edit.
Maybe HuFlungDu could put this in his post.
Originally posted by Ultimaximus
Yes yes, when all the routines are already made for you so only copy-pasta is necessary. Try coding one from scratch otherwise. β)

Well, the point is it isn't hard to make them for us end users. Which is why we thank smkdan and edit. Besides, you do sometimes still have to edit the routines, for instance when I made a routine that would grab from different GFX depending on a RAM address. That was pretty nifty :).
Originally posted by Ultimaximus
EDIT: If it's no worry, do you think you could do something about the table stretch?

Fixed it a little bit.
Originally posted by RealLink
I think you mean this tool by edit.
Maybe HuFlungDu could put this in his post.

Naw, it's here, my tutorial is not to show how to make graphics, but to show how to code the sprites themselves.
Originally posted by HuFlungDu
each frame takes up 1 32x32 square. You can also do less, but I’m sure you can figure that out for your self.

Explanation please.

Also, would it happen to be possible to move the area the GFX is DMA'd to? If so, and you know how, could you explain it?
Now with extra girl and extra hacker
Originally posted by Noobish Noobsicle
Explanation please.

Don't you already know how to make dynamic sprites? Anyways, just change the table with 00,02,20, and 22 in it to only be the tiles you want, where 00 is the top left of the 32x32 square it makes, 02 is the top right, 20 is the bottom left, and 22 is the bottom right. Unless you were asking how to make it take up less tiles in SP4, in which case it should be a matter of changing the DMA ROM -> RAM routine to only do 2 uploads, instead of 4. I haven't tested that though, I'm just guessing, take it as you will.


Originally posted by Noobish Noobsicle
Also, would it happen to be possible to move the area the GFX is DMA'd to?

Yes. I'm almost certain of it.
Originally posted by Noobish Noobsicle
If so, and you know how, could you explain it?

I don't know how, but I'll take a wild stab in the dark. I'm going to guess it has something to do with !SlotDest, so consider changing the values that are stored there within the routine.
Ah yes, that explains a lot. I just learned to make dynamic sprites a few days ago, (read: day new co-op sprite was released) and I more or less got everything I know by examining other sprites.

Thanks, I'll do a bit of experimentation.
Now with extra girl and extra hacker
Nice, this helped with a few things I was completely sure about.

Originally posted by HuFlungDu
Unless you were asking how to make it take up less tiles in SP4, in which case it should be a matter of changing the DMA ROM -> RAM routine to only do 2 uploads, instead of 4. I haven't tested that though, I'm just guessing, take it as you will.


Yeah, that's what I did with my Luigi sprite to do that. In addition, the GETSLOT routine should have 2 additional LSRs after the one it already has so it can it find every tile in the bin file, instead of only the first tile of every 32x32 block.
Just a little update to this, this is something I made for someone a while ago. Essentially it's a dynamic dynamic sprite. What does that mean? It uses a different GFX file depending on a RAM address (which you can obviously set yourself). Looks a little something like this. Now, that's just laying it out there, so here's an explanation. It needs to use manual GFX insertion, so if you don't know how to do that, stop reading and learn how. Now, in the beginning you see:

Code
!GFXADDR1 = $138000
!GFXADDR2 = $148000
!GFXADDR3 = $158000
!GFXADDR4 = $168000
!FREERAM = $066A


The 4 different GFXADDR's are the places in ROM that the different GFX files you are using are inserted. Set as many of these as you need GFX files. !FREERAM is simply the RAM that will decide which GFX file to use (I assume you can change it to use a sprite table if you need it to be different for different sprites) Farther down in the code is where my code starts (most of it is edit's), which looks like this:

Code
SEP #$20		;THIS IS WHERE MY CODE STARTS! BLING, BLING, NEON SIGN!
	LDA !FREERAM		;This is the RAM you want to set
	ASL
	PHX
	TAX
	REP #$20
	LDA GFXPointerstable,x
	DEC
	PHA
	SEP #$20
	RTS
	GFX1:
	PLX
	REP #$20
	LDA.w #!GFXADDR1&$FFFF
	CLC
	ADC !TEMP	;add frame offset	
	STA !SLOTPTR	;store to pointer to be used at transfer time
	SEP #$20	;8bit store
	LDA.b #!GFXADDR1/$10000
	JMP GFX
	GFX2:
	PLX
	REP #$20
	LDA.w #!GFXADDR2&$FFFF
	CLC
	ADC !TEMP	;add frame offset	
	STA !SLOTPTR	;store to pointer to be used at transfer time
	SEP #$20	;8bit store
	LDA.b #!GFXADDR2/$10000
	JMP GFX
	GFX3:
	PLX
	REP #$20
	LDA.w #!GFXADDR3&$FFFF
	CLC
	ADC !TEMP	;add frame offset	
	STA !SLOTPTR	;store to pointer to be used at transfer time
	SEP #$20	;8bit store
	LDA.b #!GFXADDR3/$10000
	JMP GFX
	GFX4:
	PLX
	REP #$20
	LDA.w #!GFXADDR4&$FFFF
	CLC
	ADC !TEMP	;add frame offset	
	STA !SLOTPTR	;store to pointer to be used at transfer time
	SEP #$20	;8bit store
	LDA.b #!GFXADDR4/$10000
	GFX:


This is what you need to mess with if you want to take away or add some GFX files, most specifically this:

Code
GFX3:
	PLX
	REP #$20
	LDA.w #!GFXADDR3&$FFFF
	CLC
	ADC !TEMP	;add frame offset	
	STA !SLOTPTR	;store to pointer to be used at transfer time
	SEP #$20	;8bit store
	LDA.b #!GFXADDR3/$10000
	JMP GFX

You need one of these for each GFX file you want to use (should generally be named GFXx, but really the naming convention is up to you). Then, at the very bottom we've got:

Code
GFXPointerstable:
dw GFX1
dw GFX2
dw GFX3
dw GFX4
;Etcettera, etcettera

These point to the routines I showed you earlier, add/take away pointeres from this table to change the number of GFX files you will use. As a suggestion, don't set the FREERAM to a higher number than you have here (Duh), as it will have weird effects.

And that's about all there is to that. There may be a better way to go about it, but I know this works.
uh. . . @edit1754 Are you sure this works?
I Tried the exact sprite he mentioned, did all that translhextion PC Offset stuff, Lunar address,
Blah Blah Blah, But is it okay that I patched dsx.asm AFTER Doing the manual GFX insertion and all that stuff?
(Inserting yiblowhardv2.asm/cfg, manual GFX insertion)
Cause. . . After I Did all of this, I DID Have the blowhard GFX, looking very bad. . . none the less, still has it's programming.
(Note, I'm using SMW Redrawn as A base for my hack, Will that work with dynamic sprites?)
Sometimes, I like to hack SMW. This is rare though, but I always try something new.


-------------------------
Image and video hosting by TinyPic

~Developer for "Mario Enters the Void", an abandoned hack. The thread for the hack can be found Here ~