"Animation engine" idea help.

Discussion of hardware and software development for Super NES and Super Famicom.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

"Animation engine" idea help.

Post by Drew Sebastino »

I've heard psychopathicteen talk about how he branches to an "animation engine" when he is done going through the code of all the objects. I'm not exactly sure how much his does, I imagine that every object slot would have information that the animation engine would look at and put on the screen in some way. One idea that I though of is that every object just has one register (16 bits) that would say what animation frame you want to display. This number would offset multiple tables thought the animation engine like the address of where the tiles start and how many tiles the object is using. One thing that's frustrating is that I'm pretty sure vram updates have to be done during vblank, so that's a bit on an inconvenience. I'm not sure how I would store the addresses for all (or a least most) of the tiles in sprite part of vram for it. The animation engine would find empty space in vram to add the new tiles, using the thing I thought of where it looks for 16x16 sized slots, or four of them for a 32x32 sized sprite. About the register in the object slots, it would also offset the table that holds addresses for a metasprite table, so it works for the metasprite creating routine. I have no idea how to translate this all into code... (the animation engine part.) The code also needs to know not to look for empty space in vram for the tiles if the object has already been created and the tiles are in vram.

How does everyone else deal with animation? Like I said, I'm aiming on just doing something like "lda #Player1Walk1" and it appearing on screen.
User avatar
Khaz
Posts: 314
Joined: Thu Dec 25, 2014 10:26 pm
Location: Canada

Re: "Animation engine" idea help.

Post by Khaz »

Personally, at the risk of repeating myself... I based my metasprite routine largely off psycopathicteen's reply to you back in December. I have buildMetasprite in my normal object loop right after I finish each object and I only iterate across all objects once.

When an object is spawned in my system, it's allocated its block in VRAM if it requires one. I'm using the DKC method (so I hear) of having 16 strips of VRAM for one object each, with one or two reserved for common objects. Each object contains a pointer to the address of its current sprite in ROM, if it has a VRAM block allocated. Whenever I change that ROM pointer, I set a bit that tells my vblank routine to DMA the new strip of sprite tiles over to that VRAM block.

So in my system, updating a VRAM-block-allocated object's image on screen really is as simple as storing the new ROM address and metasprite label into the object's internal registers. If it's a common object with no VRAM allocation, you either just change the tile number or write a new tile to that slot during vblank.

I know you could have problems with object-object interactions when drawing as you go through the first time, but I can't imagine them being too hard to just patch around and they'd mostly just be visual errors only visible on one frame, so hardly noticeable, right?
psycopathicteen
Posts: 3001
Joined: Wed May 19, 2010 6:12 pm

Re: "Animation engine" idea help.

Post by psycopathicteen »

I do have something similar to a "frame number register." In Alisha's Adventure, I use two registers, one that holds a "metasprite number" and another that holds a "graphics address offset" number. The "graphics address offset" is there, so I can reuse the same metasprite for multiple frames, by just changing where in ROM it's DMAing the patterns from.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: "Animation engine" idea help.

Post by Drew Sebastino »

Khaz wrote:Personally, at the risk of repeating myself... I based my metasprite routine largely off psycopathicteen's reply to you back in December. I have buildMetasprite in my normal object loop right after I finish each object and I only iterate across all objects once.
Why does that matter at all? I already have a metasprite building routine, I just want to have it to where I offset a table with metasprite table addresses so I can also do that with the vram slot finding thing. That's easy to implement.
Khaz wrote:When an object is spawned in my system, it's allocated its block in VRAM if it requires one. I'm using the DKC method (so I hear) of having 16 strips of VRAM for one object each, with one or two reserved for common objects. Each object contains a pointer to the address of its current sprite in ROM, if it has a VRAM block allocated. Whenever I change that ROM pointer, I set a bit that tells my vblank routine to DMA the new strip of sprite tiles over to that VRAM block.
See, you don't have the problem because tile positions in vram are always going to be next to each other.

Basically, I have a number I store in an object slot (lets call the slot "Joe") that at the start of the vram finding routine, is loaded to x and offsets a table that has the addresses of tables that say position of where the tiles for the frame are. In the metasprite routine, the number from "Joe" is loaded again and also offsets a table that has the addresses of the metasprite tables. Maybe this is just too much going on...

Code: Select all

Animation:

ldx Joe
lda TileAddressTableTable,x
tay
lda TileAddressTable,y

Metasprite:

ldx Joe
lda MetaspriteTablePositionTableTable,x
tay
lda MetaspriteTablePositionTable,y
sta MetaspriteCount
iny
iny
sty MetaspriteOffset
User avatar
Khaz
Posts: 314
Joined: Thu Dec 25, 2014 10:26 pm
Location: Canada

Re: "Animation engine" idea help.

Post by Khaz »

Well correct me if I'm wrong, but it sounds to me like all you're talking about is combining the metasprite number and the tile source in ROM into a single "image ID" value that will represent that exact frame of animation. I just don't see the point in that; it might save you a byte or two in each object slot, but at the expense of more space and time for the lookup tables to convert your "image ID" into a metasprite and a ROM address.

I see what you're saying about trying to dynamically search for room in VRAM to fit new sprites in, but I'm a bit wary of that approach taking up too much processing and just plain being more of a pain. But in terms of how to implement it, it doesn't really seem much different from my approach. You could still probably store every image sequentially in ROM so you only need the start address and load the right number of tiles.

The hard part of that approach is keeping track of where everything is in VRAM. You'll need several bytes inside each object to track that if you're storing one metasprite in non-sequential blocks of space. If you have multiple tile sizes you'll also be prone to having wasted space, though I'm sure that happens no matter what you do. If you have large object slots with lots of spare bytes, I could see doing things that way and maybe squeezing out a bit more VRAM room.

I dunno. I'll just give my opinion and say that it just doesn't seem like you'll get any noticeable benefit out of it for all the work and time and processor load, as opposed to a simpler system.
Last edited by Khaz on Fri May 08, 2015 9:51 pm, edited 1 time in total.
psycopathicteen
Posts: 3001
Joined: Wed May 19, 2010 6:12 pm

Re: "Animation engine" idea help.

Post by psycopathicteen »

Just give each object slot a designated tile-name table.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: "Animation engine" idea help.

Post by Drew Sebastino »

Well, I think I've kind of settled on what I want to do. I was originally using a "MetaspriteTableSize" byte in every one of my object slots along with a "MetaspriteTableOffset", but I'm just having it to where the first byte in the metasprite table is what says the size. As you can see below, the performance is pretty much the same, and I save on a word in ram and I also save on an extra load and store, so performance is probably slightly higher. I'm just going to have a separate register for the tile location, so I'll have 2 things instead of 3.

Code: Select all

Original Code:

  ldx MetaspriteTableOffset  (It actually needs to be loaded into x for the code)
  lda MetaspriteTableSize
  sta a:MetaspriteCount

New Code:

  ldx MetaspriteTableOffset
  lda a:Zero,x  (but it had proven itself useful here)
  sta a:MetaspriteCount
psycopathicteen
Posts: 3001
Joined: Wed May 19, 2010 6:12 pm

Re: "Animation engine" idea help.

Post by psycopathicteen »

My metasprite data format actually contains information on animation, as well as metasprite stuff.

Code: Select all

Alisha_stance:
dw $0018,$0000,$0004
dw $4000,$00c0,$0006,$0008
dw $0002,$0010,$fff0,$3002
dw $0002,$0010,$0010,$301a
dw $0001,$fff8,$ffe8,$3000
dw $0001,$fff8,$fff8,$300c
dw $0001,$fff8,$0008,$3018
dw $0001,$fff8,$0018,$3024
dw $0000
What these numbers mean is this:

ntsc frames per animation loop, unused, ntsc frames per animation frame
graphics ROM address, graphics ROM bank, image width, image height
sprite size, sprite X, sprite Y, attributes and number of tiles offset from ROM address
repeat until sprite size is 0

For rotating sprites, it is similar.

Code: Select all

head:
dw $8000,$00c1,$0002
dw $0000,$007f,$0004,$0004
dw $0002,$0000,$0000,$3400
dw $0000
graphics ROM address, graphics ROM bank, size of rotation steps
rotation buffer address, rotation buffer bank, window width, window height
sprite size, sprite X, sprite Y, attributes and number of tiles offset from rotation buffer address
repeat until sprite size is 0
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: "Animation engine" idea help.

Post by Drew Sebastino »

I would have though things like animation loops would have been done in the object's code.
psycopathicteen
Posts: 3001
Joined: Wed May 19, 2010 6:12 pm

Re: "Animation engine" idea help.

Post by psycopathicteen »

Yes. The looping part is done as part of the of the object code.

Code: Select all

animate_object:

ldy {metasprite_request}
cpy {metasprite}
beq +

stz {animation_frame}
+;

cpy #$0000
beq +
sep #$20

lda {animation_frame}
sta $4204
stz $4205
lda $0004,y
sta $4206
nop #8

lda $4214
sta $4202
lda $000a,y
sta $4203
nop
lda $0008,y
beq non_dynamic_animation

lda $4216
sta $4202
lda $000c,y
sta $4203

nop #3
rep #$20
lda $4216
asl #5
sta {graphics_address_request}
-;
lda {animation_frame}
inc
sta {animation_frame}
cmp $0000,y
bne +
stz {animation_frame}
+;
rts

non_dynamic_animation:
rep #$20
lda $4216
asl #4
sta {attributes}

stz {graphics_address_request}
stz {name_offset}
bra -
BTW, now that I look at it, this routine looks kind've slow. I could probably optimize it a little more.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: "Animation engine" idea help.

Post by Drew Sebastino »

I just thought of something, and I want to see if anyone approves. So you have a table with information on it relating to animation data that you index. The first word of the table says the bank the graphics are in. Everything but the first word will be addresses for tiles, but there will be special values that the animation engine will determine as different things (tile addresses obviously can't be these) The table gets indexed one word higher every fame (there will be an "animation timer" word in each object slot) the object still exists.


#$0000: End of Table Still Frame (The frame is displayed forever, as the animation timer never increases)

#$0001: End of Table Loop (The animation engine goes back to the second word in the table for an animation loop, and the animation timer is changed appropriately)

#$0002: No Animation Update (Makes the animation engine increase the timer by two and skips to the next object, or something like that)

Maybe Possible:
#$0003: Blank Frame (I'm not sure how this would work, but it would somehow mess with the metasprite routine)


I'm not exactly sure how I'm going to make it make sure to not count these values as addresses and how to do the appropriate action with each, but I'll get to it sometime.


Here are two example of how a table would look using this format:

#$0004 (bank 4)
#$916F (nonsense address)
#$0002 (same frame as before)
#$0002 (same frame as before)
#$6C87 (nonsense address)
#$0002 (same frame as before)
#$0002 (same frame as before)
#$0002 (same frame as before)
#$3B9E (nonsense address)
#$0002 (same frame as before)
#$0001 (loop to the beginning)


#$0007 (bank 7)
#$7D0D (nonsense address)
#$0002 (same frame as before)
#$0002 (same frame as before)
#$0002 (same frame as before)
#$0002 (same frame as before)
#$1805 (nonsense address)
#$0002 (same frame as before)
#$0002 (same frame as before)
#$4B6F (nonsense address)
#$0002 (same frame as before)
#$0002 (same frame as before)
#$F781 (nonsense address)
#$0002 (same frame as before)
#$0002 (same frame as before)
#$0002 (same frame as before)
#$9A76 (nonsense address)
#$0000 (do nothing but stop)
UnDisbeliever
Posts: 77
Joined: Mon Mar 02, 2015 1:11 am
Location: Australia (PAL)
Contact:

Re: "Animation engine" idea help.

Post by UnDisbeliever »

Espozo wrote:I just thought of something, and I want to see if anyone approves...
I like it. It's kinda similar to the format I have designed.

I use a different method to handle the loops. I have a command called DECREMENT_INDEX and it uses a two byte parameter which the index is decremented by. This allows for starting animations which continue into loops (ie, StopWalkingLeftAndBlink). It also saves 2 bytes as I don't need to store the starting address of the current animation.

----

While were all showing off our formats. I'll show you mine.

My format uses a bytecode syntax to prevent the linker from assigning a tileset/frame address <= $0008 (something that is possible with HIROM mapping).

I also don't store the tileset Bank in the animation list. That is set during entity load/Init (limiting me to 64KiB of tiles per entity, but I doubt I'll need more).

The current bytecodes I've designed are:
  • LOAD_LEFT_TILES - Load 4 16x16 tiles to the left half of the entity's VRAM tiles.
  • LOAD_RIGHT_TILES - Load 4 16x16 tiles to the right half of the entity's VRAM tiles.
  • LOAD_PALETTE - Copies 16 colours into CGRAM (I'm not entirely sure how to handle this one)
  • SET_METATILE_FRAME - Address of the table of the sprites frame.
  • SET_ENTITY_SIZE - Sets the bounding box for collisions (crouching)
  • WAIT_ONE_FRAME - Does nothing for one frame.
  • DECREMENT_INDEX - Decrements the animation table position by # bytes.
Here is an example of my format using DECREMENT_INDEX to handle two animations:

Code: Select all

Animation_Player_WalkLeft:
	.byte	Animation::LOAD_LEFT_GRAPHICS
		.addr		Player_HeadTiles
	.byte	Animation::LOAD_RIGHT_GRAPHICS
		.addr		Player_WalkTiles

_Animation_Player_WalkLeft_Loop:
	.byte	Animation::SET_FRAME
		.addr		MetaSpriteFrame_Player_WalkLeft1
	.byte	Animation::WAIT_FRAME
		.byte 3
	.byte	Animation::SET_FRAME
		.addr		MetaSpriteFrame_Player_WalkLeft2
	.byte	Animation::WAIT_FRAME
		.byte 3
	.byte	Animation::SET_FRAME
		.addr		MetaSpriteFrame_Player_WalkLeft3
	.byte	Animation::WAIT_FRAME
		.byte 3
	.byte	Animation::DECREMENT_INDEX
		.word	* - _Animation_Player_WalkLeft_Loop
Last edited by UnDisbeliever on Sat May 16, 2015 10:57 am, edited 1 time in total.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: "Animation engine" idea help.

Post by Drew Sebastino »

UnDisbeliever wrote:I also don't store the tileset Bank in the animation list. That is set during entity load/Init (limiting me to 64KiB of tiles per entity, but I doubt I'll need more).
You mean you're only going to use one bank for all the sprite tiles?

Wait a minute! I didn't even tell it anything on multiple sprites! Should I have it to where the values in the table are actually loaded to index a table with the addresses of other tables? The table might say the sprite size of each sprite and the tiles for every sprite would be set up a certain way so it always works. You could have it like this maybe: (The code wouldn't have to differentiate these numbers with addresses because it will always alternate)

#$0000: End of Table
#$0001: Small Sprite Size
#$0002: Large Sprite Size

A table would look like this:

#$0001 (Small Sprite)
#$6D89 (Nonsense Address)
#$0002 (Large Sprite Size)
#$F7C4 (Nonsense Address)
#$0001 (Small Sprite)
#$37DE (Nonsense Address)
#$0001 (Small Sprite)
#$5AA7 (Nonsense Address)
#$0001 (End of Table, No Sprites Left)

Maybe this is getting out of hand...

One thing though UnDisbeliever, are you coding in C? I think I remember looking at the source code for one of your games and it didn't look like anything I was used to.
UnDisbeliever
Posts: 77
Joined: Mon Mar 02, 2015 1:11 am
Location: Australia (PAL)
Contact:

Re: "Animation engine" idea help.

Post by UnDisbeliever »

Espozo wrote:You mean you're only going to use one bank for all the sprite tiles?
No, each entity type (tank, walker, player, stomper) has their own Bank, leaving up to 64KiB tiles (2048 8x8 tiles) per enemy type. I may change this in the future.
Espozo wrote:Wait a minute! I didn't even tell it anything on multiple sprites... You could have it like this maybe
I've tried to parse that paragraph a few times and I don't really understand it.

Is this what your trying to explain?

Code: Select all

repeat
	vramPos = sprite VRAM position.
	command = table[pos]
	pos += 2

	if command == 0
		break loop

	else if command == SmallSprite
		DelayedCopytoVRAM(bank, table[pos], vramPos, 16)
		pos += 2
		vramPos += 16 / 2

	else if command == LargeSprite
		DelayedCopytoVRAM(tile bank, table[pos], vramPos, 32)                // top half
		DelayedCopytoVRAM(tile bank, table[pos] + 32, vramPos + 16 * 16, 32) // bottom half
		pos += 2
		vramPos += 32 / 2
		if vramPos & $0100
			// move to next line so you don't override the large tiles
			vramPos += $0100
And each command loads a single small or large tile into VRAM?

I think its going to eat quite a bit of ROM space if you do that. I think you should add a size parameter to it. My version 1.0 is going to use a fixed size parameter size of 256 bytes (128 bytes (8 small tiles) to the top half, 128 bytes for the lower half).


---
Espozo wrote: One thing though UnDisbeliever, are you coding in C? I think I remember looking at the source code for one of your games and it didn't look like anything I was used to.
No, I'm coding using structure macros to bring a bit of High Level Assembly to the 65816.

These macros turn code like this:

Code: Select all

	LDA	z:WES::walkLeftOnZero
	IF_ZERO
		LDA	#JOY_LEFT
	ELSE
		LDA	#JOY_RIGHT
	ENDIF
into this:

Code: Select all

	LDA	z:WES::walkLeftOnZero
	BNE __STRUCTURE_001
		LDA	#JOY_LEFT
		BRA __STRUCTURE_002
__STRUCTURE_001:
		LDA	#JOY_RIGHT
__STRUCTURE_002:
After I have written, tested and optimised the code I verify its correctness by converting it into psudeo-code so that 'future me' can quickly understand it in order to fix bugs.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: "Animation engine" idea help.

Post by Drew Sebastino »

UnDisbeliever wrote:I've tried to parse that paragraph a few times and I don't really understand it.
Basically, each frame of animation in each object consists of multiple sprites. It needs to look through vram to find an empty slot for each sprite in the frame, and the result of where it was finally found is transferred to the character bits in the metasprite routine. The reason you need to tell it if it's big or not is that it has to find a slot that size in vram. Do you get it now?
UnDisbeliever wrote:Is this what your trying to explain?
I don't get that at all. :oops:
No, I'm coding using structure macros to bring a bit of High Level Assembly to the 65816.
Weird...
Post Reply