Thanks for all the replies, it's very interesting to hear how you all have gone about this task!
It seems so far that I am the odd one out, still sticking to my hard coded animations rather than using lookup tables. As I mentioned before, I have tried switching to using LUTs but abandoned it when I was afraid it would limit my control over animation priority (if the player is running AND shooting, which animation should play?) and fluidity. But I suppose I could always do hard coded overrides if I run into issues like that.
Here's an example of how I formatted those LUTs:
Code: Select all
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;; Animation_lists ;;;;;;;;
player1_animations:
@size: .db 7
.dw @dying
.dw @hit
.dw @throwing
.dw @ducking
.dw @jumping
.dw @running
.dw @idle
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;; Animations ;;;;;;;;
@running:
.db 4 ; animation length in cels.
.db 2 | ANIMATION_LOOPING ; here i store a value for speed and a flag for looping.
; the speed is essentially a divider for the animation timer
; used to index the list of frames below. this means the higher the value - the slower the animation
.dw playersprite1_run_01 ; pointer to metasprite
.dw playersprite1_run_02
.dw playersprite1_run_01
.dw playersprite1_run_03
@dying:
.db 6
.db 3
.dw playersprite1_dying_01
.dw playersprite1_dying_02
.dw playersprite1_dying_01
.dw playersprite1_dying_02
.dw playersprite1_dying_03
.dw playersprite1_dying_04
@idle:
.db 1
.db 1
.dw metasprites_player1_0
@hit:
.db 1
.db 1
.dw playersprite1_hit_01
@ducking:
.db 1
.db 1
.dw playersprite1_duck_01
@jumping:
.db 1
.db 1
.dw playersprite1_jump_01
@throwing:
.db 2
.db 2
.dw playersprite1_throw_01
.dw playersprite1_throw_02
So what I did was every frame I decide which animation should be playing depending on what state flags are activated for the player. I then use the players animation timer (which is incremented every frame) divided by the "speed" byte for selected animation as an index of what frame I should render. I thought I was kind of clever to do it that way but it also limits me in that every metasprite of an animation is displayed for an equal amount of time. It also limits me to having animations lasting no longer than 256 frames (VBlanks, not actual animation frames), as the timer is incremented every frame.
FrankenGraphics wrote:I think implementation and format are kind of inseparable things.
I agree and I think laying out the format is an easier way to explain the system by it's own than it would be to lay out the actual code reading it.
It kinda felt to me like koitsu confused your animation format with actual metasprite data though, but I may very well be wrong.
never-obsolete wrote:I keep three arrays for animation handling:
Object_Animation - the current animation number
Object_Frame - the current frame number in that animation
Object_AnimTimer - the timer for the current frame
It seems you all seem to go for a frame countdown rather than a timer for the animation as a whole, which I found interesting as it would add another byte in ROM to every cel of animation. Keeping track of which animation and which frame is played for each entity also first seemed kind of wasteful in RAM to me but I guess it speeds up the logic of progressing to the next frame when the countdown reaches 0.
Maybe I'm becoming a little bit too concerned about RAM and ROM usage, though... I guess even if ROM space is cheap I'm worried about having to spread things out in too many banks but maybe I should worry about that when it actually becomes a problem.
I will most likely try my hand at a LUT based animation system again and I think I'll implement some of the design choices you've all laid out. Having a countdown for each frame seems like a sensible way of gaining maximum control of the animation's fluidity without doing hard coded stuff like I am. It would also take away the limitation I have of an animation only being able to last a few seconds - those same seconds would now be able to fill out a single cel if needed.
FrankenGraphics wrote:For instance, project blue assumes 4 cels per object and animation, and 4 tiles per cel.
So does that mean your metasprites are limited to only 4 tiles unless you combine multiple entities? Is that because you store the metasprites in the actual animation sequence, and if so, would it not be better to instead store two bytes as pointers to a metasprite of any size?
gauauu wrote:Like your control codes, I also have a control code (followed by an address) for "switch to another animation" so that an animation can start, run a few frames, then jump to a new animation.
I'm guessing this feature is mostly used to chain together multiple animations where 4 cels are not enough? Is there a reason why you have fixed length for the LUTs? Is it so you can store them in parallel? Hmmm.. but even if that would be the reason, you should be able to just have a header for each animation with the length stored. I'm not trying to criticise your choices, I'm just very interested as I've not had the opportunity to discuss this topic with anybody else.
Thank you all for the replies so far! Once again, I find it very interesting and I would love to hear more from as many people as possible.
Beers to that! I'm going to my fridge right now.