Ways of implementing sprite animations

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Ways of implementing sprite animations
by on (#236603)
Hey everyone! I'm curious how you all go about implementing sprite animations in your projects.

Personally I've taken the route of storing states for the entity in question and a timer to determine what frame should be loaded. I then go about writing logic that ultimately stores pointer values to be used by a generic sprite loading routine. A simplified excerpt from my code:
Code:
LoadPlayerSprite:
   
   ; variables:
   ; curr_entity_state_1      ; set before entry representing the player's state
   ; OAM_currSpriteAnimTimer   ; set before entry and later used by the generic routine "LoadSprite"
   
   lda curr_entity_state_1
   and #PLAYER_WALKING
   beq @NotWalking

      lda OAM_currSpriteAnimTimer
      cmp #PLAYERSPRITE_RUN_01_TIME
      bcc @SetFrameToRun01

      cmp #PLAYERSPRITE_RUN_02_TIME
      bcc @SetFrameToRun02

      cmp #PLAYERSPRITE_RUN_03_TIME
      bcc @SetFrameToRun01

      jmp @SetFrameToRun03
      
   @NotWalking:
   
   ....
   
   @SetFrameToRun01:
   lda #<(playersprite1_run_01)
   ldx #>(playersprite1_run_01)
   jmp LoadSprite
   
   @SetFrameToRun02:
   lda #<(playersprite1_run_02)
   ldx #>(playersprite1_run_02)
   jmp LoadSprite
   
   @SetFrameToRun03:
   lda #<(playersprite1_run_03)
   ldx #>(playersprite1_run_03)
   jmp LoadSprite
   
   ....
   
LoadSprite:

   ; loads metasprite data into $0200, using a and x as pointers
I have code like this for all entities.

I tried replacing all that logic with lookup tables instead and got it working but started to question whether it was just making things more complicated.
Now I'm thinking about it again, though. My reasoning being that by having a generic routine for loading a frame for any given entity type and timer, I would be able to write a tool to create animations from a set of metasprites. Doing it that way might alleviate me from having to write most of the animation code (the animation state machine, I guess you would call it) for each individual sprite entity. But by doing so I guess I would also lose full control of the animations.

So, how do you guys and gals go about sprite animations? Do you write all the logic for which frame to display by hand or do take a more tricky route?

Cheers! :beer:
Re: Ways of implementing sprite animations
by on (#236611)
my personal preference: raw oam-format data, optionally with a termination byte or sequence at the end of each cel. table with cel pointers... and the sequence is a base addr, an index into that LUT and a duration for each index. you could keep them in parallel and reuse the x reg, or you could interleave them. Exactly how you read those LUTs is really up to how complex animation sequencer functions you care to write. Eats lots of ROM but won’t give you ”i wish i could have it look like this” type of regrets later on. ROM is cheap! Your designs are not, since they take time.

You may use the MSB in Ypos and Xpos for something if you so wish since it is rare for an object to be that spread out.
Re: Ways of implementing sprite animations
by on (#236614)
@FrankenGraphics -- I think the OP is asking how one goes about implementing actual sprite animation, i.e. code/logic used for deciding how/when to update the data DMA'd to OAM (thus triggering, visually, an actual change in the sprite), and how to implement (sanely) frame delays on a per-sprite basis -- not so much the internal format used for sprite organisation. The "how complex animation sequencer functions" part of your post is what they're asking about.

This subject is something I imagine every single person/coder struggles with, so it's an excellent question. I'd be curious to read answers from those who have done games (ex. rainwarrior, tepples, others.)
Re: Ways of implementing sprite animations
by on (#236615)
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

The structure of my animations are:

Code:
  [frame id], [timer]
  [frame id], [timer]
  ...
  [control code]


The control codes decide if the animation should hold on the last frame, loop to the beginning, or reverse back to the beginning. Each time the drawing code is called, Object_AnimTimer is decremented and if zero, Object_Frame is incremented and Object_AnimTimer is loaded with the new value.

The objects state handler can then set the animation by calling Func_ChangeAnimation with the animation in the A register and the objects id# in the X register:
Code:
         ; ... code that determines player is standing still

         ldx Object_Direction               ;   set to standing animation
         lda plyrstand_animtbl, X
@change_animation:
         ldx #0
         jsr Func_ChangeAnimation

         ; ... other stuff
Re: Ways of implementing sprite animations
by on (#236621)
koitsu wrote:
This subject is something I imagine every single person/coder struggles with, so it's an excellent question. I'd be curious to read answers from those who have done games (ex. rainwarrior, tepples, others.)


Really, it's so specific to the game requirements that I've done it differently with every game I've done.

never-obsolete wrote:
I keep three arrays for animation handling:

Object_Animation - the current animation number
....
The control codes decide if the animation should hold on the last frame, loop to the beginning, or reverse back to the beginning.


In my current project (which Frakengraphics is making metasprites for) I do almost the exact same thing as this, only I use a pointer to an animation's address instead of a number.

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.


On a completely separate thought, but worth mentioning -- I also (based on a tip from someone here, but unfortunately I can't remember who) found that setting a metasprite's anchor significantly to the left and either above/below the sprites, can make clipping easier. When you need to check each sprite to see if that sprite went offscreen (when the metasprite is partially onscreen), it's easier if each sprite is ALWAYS the same direction from the anchor point.

(In practice, she will design the metasprite with a reasonable anchor point like you'd expect, and I pre-process each one at build time, to adjust the anchor point for this optimization)
Re: Ways of implementing sprite animations
by on (#236648)
I think implementation and format are kind of inseparable things. For instance, project blue assumes 4 cels per object and animation, and 4 tiles per cel. You may get some variation out of each such set, SMB style, by altering the durations, but if you want it do do something different, you need to switch object ID. If you need bigger entities, you need to construct them from multiple objects.
A bullet is held on its first cel and the wall-collided destruction is kept in the remaining three. Most other objects cycle through their cels.

You could for instance let a duration of 0 imply no forward progression. You could then have a subroutine to forward one step manually to create one-shot animations, since stepping forward and cycling is otherwise implied.

The pro is you can keep it simple. The con is that if you need comlex actors/animations, you'll eat object ID:s quickly.

NESmaker assigns one animation type and one speed to each so-called action step* (of which each object may have up to 8). Since it was written for topdown games primarily, each animation type has up to 8 variations for each direction, which can be exploited for other things in a non-topdowner. Durations are set with each action animation type, or at events which overwrite what action step is due and at what animation speed. You can't vary the duration within an animation sequence easily, except by setting the animation speed high and referring to the same cel multiple times to act as duration.
Again in this engine, if you need more than 8 action steps you need to construct your entity from several object ID:s that replace each other on events. However, this wouldn't be too common.

While the engine assumes cycling/looping animations, for a flower that burts it bud, i worked within the present system and used one action step to cycle a 1-cel animation of it being closed, another action step for a couple of cels of bursting, and at the end of animation i call a 3rd action step that loops another single cel that keeps the flower open.

*An action step is basically an AI unit comprising of an action, an end of action alarm and an optional end of animation alarm. It can be "walk current direction then turn in the general direction of the player" or "wait this long, then increment action step if this condition is fulfilled, else go to the first action step" or something like that. The system is good for rulebound patterns but not too good at reactions and priorities as-is.


I think it's fair to categorize the object complexity needs in three categories.

-main character, maybe bosses - lots of flexibility
-typical goons - medium complexity
-dumb objects, like projectiles, pickups, overlay details, objects that the player/good/bosses use, maybe even NPC:s and stationary goons.

In addition to this, you could have a fourth category labeled inanimate, which would include quite a few of the 'dumb' ones.

It might make sense to simplify AI handling, collision handling AND animation handling for the 3rd category.

The first category may have a handling category of its own, or you may reserve several object ID:s for the same entity to create modes this way. Bosses might have components that work on either level.
Re: Ways of implementing sprite animations
by on (#236658)
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:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;; 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. :beer:
Re: Ways of implementing sprite animations
by on (#236661)
I have a table of 'cells' which are single frames of an animation sequence, stored as raw OAM data, 4 bytes per 8*8 tile (I like the flexibility of not having to align all the sprite's tiles to a perfect grid--many of my sprites have tiles that overlap). Then I have an animation table where each animation sequence is a series of Cell indices, how many cells are in that animation sequence, how many frames each cell should last, and attribute data for that cell such as flipping and palette changes. each object has a byte of RAM for the current animation sequence, the current cell index, and an animation timer that counts down to 0. when it reaches 0, it loads the next cell and resets the timer for that cell's length. when it reaches the last cell, it loops.

As far as how I determine which animation sequence an object should be displaying at any given time based on its actions, I don't use look up tables directly for that. Instead, I use the animation index as a sort of quasi 'state' variable. for example, if the player object is jumping, I have a logic script that determines that and assigns the jumping animation to it. if I need to check later on whether it's jumping or not, I can look at the animation sequence byte and know based on which index it's using. It's convoluted, but it works well enough.
Re: Ways of implementing sprite animations
by on (#236663)
pwnskar wrote:
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?

Basically yes to all.

The con i mentioned is under the circumstance that an entity switches object ID to show another animation and have other physics properties (such as climbing or jumping/falling). That's not too bad, or rather it would depend on how big a table of object ID:s you can have, and how much of it is shared vs specific to levels.

The bigger, unmentioned con is that for the bosses (which are bigger than 2x2 tiles) eat up quite a bit of cpu time each frame since each 2x2 piece is being handled separately. You also need to take some care with both AI and boss room layout since they're subject to vs. background collisions individually. This is not recommended.

It can be convenient for some games to have a very rulebound distribution of sprites, though. Project Blue just happened to grow outside its original scope considerably. I'm largely at fault for exploding the goalpost and delaying the final game. Toggle_switch is the one responsible for reminders that that the game ought to be released at some point. :wink:

The PrBlue engine is also able to put the tiles in other configurations too, not just 2 by 2. For the upward air streams in maps 2 and 3 they're stacked in 1x4 columns. But the max is 4 sprites per metasprite.
Re: Ways of implementing sprite animations
by on (#236671)
pwnskar wrote:
It seems so far that I am the odd one out, still sticking to my hard coded animations rather than using lookup tables.


I've done that also in some cases. (which is why I mentioned that it varied so much with specific game requirements) It really depends on the game and situation. In my current game, I have a bit of hard coded animation for pieces that work better that way. In my previous two years' nesdev compo game, I hard-coded all the animations because there weren't very many unique animations, and it was easier that way. But for my current game with a lot of enemies, having data-driven metasprites makes it a lot easier for an artist (frankengraphics) to create, edit, and test animations without us having recode things.

pwnskar wrote:
I'm guessing this feature is mostly used to chain together multiple animations where 4 cels are not enough?

It's more useful for animations that always flow into another one -- things like "landing from a jump into standing idle", where "standing idle" is already an animation. But it can also be useful for chaining when I need more cels, see below.

Quote:
Is there a reason why you have fixed length for the LUTs?

I don't have a limit of 4 cels (that was Frankengraphics' other game), but I do have a limit of 64 sprites per animation definition (ie 256 bytes). This way, for each entity, I store the address of the animation, and the offset into the frame data, and can use an indirect indexed lookup to quickly get each byte of the animation. (lda (ptr),y). It's fast, and 64 sprites is enough for most things.
Re: Ways of implementing sprite animations
by on (#236746)
gauauu wrote:
I've done that also in some cases. (which is why I mentioned that it varied so much with specific game requirements) It really depends on the game and situation. In my current game, I have a bit of hard coded animation for pieces that work better that way. In my previous two years' nesdev compo game, I hard-coded all the animations because there weren't very many unique animations, and it was easier that way. But for my current game with a lot of enemies, having data-driven metasprites makes it a lot easier for an artist (frankengraphics) to create, edit, and test animations without us having recode things.

Yeah, I'm thinking it will make things easier for me too, even though I'm doing both code and art myself. I used to write in my metasprites by hand, thinking it would go much faster to keep doing it like that rather than writing a tool to convert msb's from NESST to the format used by my engine. But once I actually wrote some scripts to help with that stuff I'm now much more prone to do iterations on my graphics. So I think doing the same for my animations will make that process more enjoyable as well.

I took a liking to the approach you all share of storing a variable for the current frame/cel index in each entity and using a countdown for when to proceed to the next one. I'm still a bit cheap on RAM though, so I've opted to not store any value for current animation and instead will be doing that every frame based on other variables until I find a need to do otherwise. Got it working on a test sprite and will update with a code snippet once it's cleaned up a bit. Maybe it could be useful for someone new to animation on the NES or other retro platforms, or helpful to me in case someone notices something that could be done better in the code.

Now I kind of feel like I have to find/create a tool to make the animation sequences. I've tried googling a bit but didn't find much. Maybe someone here would know if such a tool has already been done and shared?
If not, it would be fun to brush up on my C# skills to make something that loads an msb file and let's you arrange metasprites in sequence with a countdown in between.

I've seen some gifs of mind blowing tools by kasumi but I don't know if he/she has shared anything related to animation sequences of existing CHR and metasprite data.
Re: Ways of implementing sprite animations
by on (#236747)
pwnskar wrote:
But once I actually wrote some scripts to help with that stuff I'm now much more prone to do iterations on my graphics. So I think doing the same for my animations will make that process more enjoyable as well.

Then, if your build step can automatically run those scripts, you can just work with your graphics in whatever format makes you happy, save them, then re-run the build. No fiddling about with exports, or remembering to convert things. It makes iterative design and testing a lot easier.

Quote:
Now I kind of feel like I have to find/create a tool to make the animation sequences. I've tried googling a bit but didn't find much.

People seems to really like Aseprite, although I've never really used it, so I have no idea how well it fits with the NES way of doing things.
Re: Ways of implementing sprite animations
by on (#236749)
gauauu wrote:
Then, if your build step can automatically run those scripts, you can just work with your graphics in whatever format makes you happy, save them, then re-run the build. No fiddling about with exports, or remembering to convert things. It makes iterative design and testing a lot easier.
Yes, already doing that. :)

gauauu wrote:
People seems to really like Aseprite, although I've never really used it, so I have no idea how well it fits with the NES way of doing things.
That looks like an interesting piece of software but it doesn't appear to have any native support to import NES CHR files or work with tiles at all?
Re: Ways of implementing sprite animations
by on (#236757)
pwnskar wrote:
That looks like an interesting piece of software but it doesn't appear to have any native support to import NES CHR files or work with tiles at all?

Correct, it doesn't. It's a pixel-focused image editor with "enhancements", but mainly intended for image formats like the following: BMP, FLC, FLI, GIF, ICO, JPG, PCX, PCC, PNG, TGA, and WEBP. You have to rely on external third-party conversion tools for the image conversion process for NES stuff. The one potential exception seems to be the Export Sprite Sheet option, which is horribly documented; one of the options you can choose there is to export JSON data, if that's at all helpful -- the JSON format itself isn't documented either, so you have to sift through it and look carefully (it contains a nested set of hashes/dicts and arrays). It has support for "extensions" but there doesn't seem to be any documentation on it, nor is there any extension that adds NES-specific features.

Overall, Aesprite is intended for commonplace PC graphic formats and not so much for video game console data formats -- don't let anyone tell you otherwise. (FYI: I'm an Aesprite owner. It's a "useful" utility, intuitive in some ways and completely non-intuitive or downright weird in others; the file-oriented dialogs are terrible, and some of its region-select features are weird/bizarre in behaviour compared to other editors. Off-topic so I'll save my comments for some other time.)

You might consider using NES Screen Tool by Shiru here on the forum instead. I haven't used it myself, but have seen infiniteneslives use it on his live streams.
Re: Ways of implementing sprite animations
by on (#236758)
I use NESST for pretty much everything except 1) sprite overlays on still pictures such as title screens and 2) animation previews. For the latter i just screen grab and paste my NESST-made metasprites in photoshop and make gif animations; 2 frames equals 1 NES frame (on PAL, NTSC would be slightly quicker). I can still step through metapsrites to kind of review animations in NESST.

You need to be a bit careful when saving, which has its quirks, and when editing metasprites, but otherwise it's really great.
Re: Ways of implementing sprite animations
by on (#236760)
pwnskar wrote:
I've seen some gifs of mind blowing tools by kasumi but I don't know if he/she has shared anything related to animation sequences of existing CHR and metasprite data.

Coming soon™ since 2018!
Image
Image
Import an image sequence, get CHR data and metasprites. No preparation needed in most cases, even if the image sequence is more than 4 colors (so long as it is less than 14 colors). (That said, you probably at least want to set up a palette file for it to use. It's capable of guessing, but if you make it guess for multiple characters, the guesses won't align which isn't good if you want them on screen together.)

You're welcome to PM me if you want to try it. It works, and is stable (albeit probably hard coded to something slightly less useful for the latest shareable version), but I've been getting the impression that what people want from it is even more impossible than what it already does. There's also a public version here: https://kasumi.itch.io/ichr But it only does backgrounds (which, yes, can be animated). It should be similarly no fuss. Just import the image and get data. No manual recoloring or anything, unless the image doesn't conform. (In which case it usually shows you where.)

Assuming your sprite were only 3 colors plus transparent, you could use the public version for that, but I wouldn't recommend it. (It has been used in this way, though.)

Edit: It exports NES Screen Tool data (provided the entire character uses less than 256 tiles), but can't import it (through the UI, anyway). I have some code for it, but there are reasons why it's not available. I've been trying to plan how best to make those >256 tile characters still be interoperable with NES Screen Tool. Re-reading your post, this is probably the main thing you want, and I slightly regret writing the rest! :lol:

Edit2: I've attached a file of what it currently exports for sprite data. I guess I'm this deep in, anyway...
Attachment:
Exported Character Files.txt [3.23 KiB]
Downloaded 206 times

Edit3: (Some things are explained elsewhere in the documentation. u is unsigned, s is signed. The number following is how many bits. All big endian.)
It can also save and load all data to a single custom file, but I feel like I'm going to regret that it exists.

As far as Aseprite being unintuitive, I really disagree. Whatever ways you find its region select features to be unlike other editors can probably be changed. (By default it's MS Paint-like, but it can be set like other things I've used.) (Unless you mean specifically for selecting/moving frames and or layers, rather than the canvas, in which case I agree.) Similarly, if you find the file oriented dialogs terrible, there's an option to use native OS file dialogs (which doesn't work on Linux Mint at least, but well... or maybe that's fixed, I haven't updated in a bit, because I haven't done pixel art in a bit.)

Granted, it has some defaults that are unlike other software (my least favorite being the canvas scaled to 200X by default, in such a way you can't see 1:1 pixels), but I'm not sure it's fair to expect it to behave like something else. I don't think it does anything egregious (like say, Pro Motion which ran counter to everything I've used in nearly every respect as opposed to a reasonable number of things.)

Extensions are for skins/palettes. It supports Lua scripting, which might be a better start for dealing with NES Graphics: https://github.com/aseprite/api Whether documentation is good depends on perspective. People seemed to take to it very quickly. Had it had that when I started my graphics tool, maybe I'd have used it.

I find Aseprite to be the least frustrating piece of software I've ever used (that wasn't... like a text editor), and it's getting better all the time. It getting better all the time, and getting better quickly is perhaps why I cut it slack. Tile support is being worked on: https://twitter.com/aseprite/status/109 ... 21?lang=en All kinds of other things are constantly being worked on, across all areas of it to give it better features, and make it more user friendly. Lua scripting seemed to come out of nowhere, and perhaps it has problems now (haven't done much with it), but I have confidence they'll be fixed. I have confidence most things in Aseprite will be fixed. Forgive me, I'm an Aseprite stan.

My on topic answer, that is neither about Aseprite, nor about my own graphics tools:

For Indivisible, animations were more or less tied to states. Each state had a duplicate of some animation playback code, but not code to check for when to advance individual frames.

Code:
lda AJNAframe,x;AJNAframetimer
   clc
   adc #%00000100
   sta AJNAframe,x;AJNAframetimer
   cmp #5*16;*16 to shift to the high bits
   bcc ajna.standingaxeless2.continue
   jmp ajna.toidle
ajna.standingaxeless2.continue:
   
   lsr a
   lsr a
   lsr a
   lsr a
   tay
   lda ajna.standingaxeless2frames,y
   sta AJNAmetasprite,x

   jmp spriteend

Usually (because, again, each state technically had its own version of this) a byte was split in half. The low bits were the frame timer, and the high bits were the frame. By adding to the low bits, the high bits would eventually change, changing the frame. Then the low bits were divided out to get the actual graphic to display, loaded from a table.

This state actually seems old, I ended up thinking of lots of clever bit hacks beyond the above to get the most out of that one byte. If you set the high bytes to $F0 - framecount*16, you can detect the end of the animation by the carry being set alone (as opposed to a cmp) and you can do similar for the lowest bits to make them advance in X frames without changing the add value.

I can certainly think of ways to make that generic (usually a state will play an animation, and then when the animation is complete go to another state), but some of Indivisible's states were reasonably meaty, and I think making them generic wouldn't have been worth it with regards to the kinds of things that needed to happen on specific frames. (During crouching Axe, a hitbox appears in the middle of the animation. You can cancel the attack by jumping if and only if that hitbox hits something, which will take effect after Ajna exits hitstop. Other attacks that hit can't be jump-canceled. Most attacks can be turned around within 3 frames of starting.)
Re: Ways of implementing sprite animations
by on (#236778)
koitsu wrote:
You might consider using NES Screen Tool by Shiru here on the forum instead. I haven't used it myself, but have seen infiniteneslives use it on his live streams.

FrankenGraphics wrote:
I use NESST for pretty much everything except 1) sprite overlays on still pictures such as title screens and 2) animation previews. For the latter i just screen grab and paste my NESST-made metasprites in photoshop and make gif animations; 2 frames equals 1 NES frame (on PAL, NTSC would be slightly quicker). I can still step through metapsrites to kind of review animations in NESST.

You need to be a bit careful when saving, which has its quirks, and when editing metasprites, but otherwise it's really great.

I really enjoy NESST too, it's what I do all my metasprite work in, apart from sketching in Photoshop. My workflow so far is to make all my tiles and metasprites in NESST, then running a python script in my compile script (*.bat file). The python script I've done is passed some parameters for what *.msb file to read and what the output should be. It then spits out the data as labeled assembly in a tailored format that I include in my game engine.

Kasumi wrote:
My on topic answer, that is neither about Aseprite, nor about my own graphics tools:

For Indivisible, animations were more or less tied to states. Each state had a duplicate of some animation playback code, but not code to check for when to advance individual frames.

Code:
lda AJNAframe,x;AJNAframetimer
   clc
   adc #%00000100
   sta AJNAframe,x;AJNAframetimer
   cmp #5*16;*16 to shift to the high bits
   bcc ajna.standingaxeless2.continue
   jmp ajna.toidle
ajna.standingaxeless2.continue:
   
   lsr a
   lsr a
   lsr a
   lsr a
   tay
   lda ajna.standingaxeless2frames,y
   sta AJNAmetasprite,x

   jmp spriteend

Usually (because, again, each state technically had its own version of this) a byte was split in half. The low bits were the frame timer, and the high bits were the frame. By adding to the low bits, the high bits would eventually change, changing the frame. Then the low bits were divided out to get the actual graphic to display, loaded from a table.

This state actually seems old, I ended up thinking of lots of clever bit hacks beyond the above to get the most out of that one byte. If you set the high bytes to $F0 - framecount*16, you can detect the end of the animation by the carry being set alone (as opposed to a cmp) and you can do similar for the lowest bits to make them advance in X frames without changing the add value.

I can certainly think of ways to make that generic (usually a state will play an animation, and then when the animation is complete go to another state), but some of Indivisible's states were reasonably meaty, and I think making them generic wouldn't have been worth it with regards to the kinds of things that needed to happen on specific frames. (During crouching Axe, a hitbox appears in the middle of the animation. You can cancel the attack by jumping if and only if that hitbox hits something, which will take effect after Ajna exits hitstop. Other attacks that hit can't be jump-canceled. Most attacks can be turned around within 3 frames of starting.)

Thank you for taking part in the discussion, and in such detail too! :beer:

I would definitely like to try your tools and I'll PM you a request about that. From what I understand your tool set is an all-in-one conversion from a sequence of images to CHRs, palettes, metasprites and animations? It would be really interesting to try that workflow. I would imagine it can speed up iterations quite a lot.

I read through the exported data example you linked and you seem to also store a delay value for each frame. But when reading your code example, I can't see where that value is actually read? It can't be "AJNAframe, x", right? Because then you would have to be using indirect addressing with the y register. Unless you've already read the value before and stored it somewhere in RAM, which would be AJNAframe?

Back to the issue of tools; as cool as it is to create all graphical content from bitmap sequences, I still feel it could be good to have a sequencer tool that dealt with CHR data and msb's natively. The more I think about, I wish there was a tab for sequencing animations in NESST, just like there's a tab for nametables and metasprites. It's been many years since I touched C++ and I never fully got the hang of it, but maybe I should have a look at the source code for NESST to see if I can add something like that. Or perhaps I'd do better to just make something in C# or python first. But from what I understand you are already working on code for your tool to import data from NESST?
Kasumi wrote:
Edit: It exports NES Screen Tool data (provided the entire character uses less than 256 tiles), but can't import it (through the UI, anyway). I have some code for it, but there are reasons why it's not available. I've been trying to plan how best to make those >256 tile characters still be interoperable with NES Screen Tool. Re-reading your post, this is probably the main thing you want, and I slightly regret writing the rest! :lol:
Re: Ways of implementing sprite animations
by on (#236806)
pwnskar wrote:
From what I understand your tool set is an all-in-one conversion from a sequence of images to CHRs, palettes, metasprites and animations?

Yes. You open an image sequence. You wait (sometimes an hour, if you're doing something insane like 255 frames of Street Fighter 3 Third Strike animation). You optionally open another image sequence for another animation. You press shift+E. You're done.
pwnskar wrote:
I would imagine it can speed up iterations quite a lot.

Making changes free to try is probably the largest benefit. Even if you eventually want to do it manually, this can help get things in game quickly to see how they'll look before committing to doing it manually.

I avoided changing certain attack animations in Indivisible for a long time, because I dreaded the work. I-CHR can do all the frames (over 200) significantly faster than I can do a single frame manually. The result is slightly worse (as far as tile use), but at this scale that doesn't matter.
Quote:
I read through the exported data example you linked and you seem to also store a delay value for each frame. But when reading your code example, I can't see where that value is actually read? It can't be "AJNAframe, x", right? Because then you would have to be using indirect addressing with the y register. Unless you've already read the value before and stored it somewhere in RAM, which would be AJNAframe?

This tool was made after Indivisible, in response to the problems I had with the process. Indivisible doesn't have different frame delays per frame, only per animation. If a frame in an animation needs to be displayed for twice as long as the other frames:
Code:
frames:
.db 0, 1, 2, 2, 3

I'm not that sure I'd support different frame delays per frame even in a new engine I might write, I don't think it'd do anything but use more CPU. The tool supports it because it was easy to do.
Quote:
Back to the issue of tools; as cool as it is to create all graphical content from bitmap sequences, I still feel it could be good to have a sequencer tool that dealt with CHR data and msb's natively.
...
But from what I understand you are already working on code for your tool to import data from NESST?

I had NESST import working so I could compare my manual Indivisible work to it, but it got broken somewhere along the way. I don't personally benefit much from fixing it. My biggest goal is to just release the update. I have lots of ideas for it, but right now I'm mostly just killed by how much better what I'm sitting on is than the public version. (The public version is still real good at what it does, though.)
Re: Ways of implementing sprite animations
by on (#237749)
Hey Kasumi and everybody else! I've had a look at I-CHR and I definitely see the power in it once one gets the workflow integrated into one's project.

As my own project is founded on a workflow using raw tile data assembled into metasprites in NESST, I decided it would be better for me to create my own custom tool.

So here it is: https://github.com/pwnskar/NESAC
Attachment:
NESAC.zip [17.17 KiB]
Downloaded 131 times

I call it "NESAC - The NES Animation Corner" and it handles animation data in a fashion that I thought seemed to be shared more or less by everyone in this thread.

Attachment:
nesac.gif
nesac.gif [ 40.65 KiB | Viewed 4505 times ]


Attachment:
file.jpg
file.jpg [ 38.56 KiB | Viewed 4505 times ]
Attachment:
tools.jpg
tools.jpg [ 19.65 KiB | Viewed 4505 times ]
Animations can be saved in either binary format or exported to an assembly file (asm6 style). I would recommend starting out by using the assembly export and compare the results to the binary format to dissect how that is stored. Given everybody probably wants the ability to have full control of how the data is finally stored in ROM, creating a build script that parses the binary animations might be optimal. You could of course also just download the source of the tool and adjust the assembly export to fit your needs.


Attachment:
metasprites.jpg
metasprites.jpg [ 26 KiB | Viewed 4505 times ]
Animation frames are stored with indexes to loaded metasprites, so I've added the ability to store labels as a separate file that can be edited through the tool or a text editor. If no labels are supplied the tool will automatically label each metasprite by itself. The way I use labels myself is I do them in NESAC, then parse the resulting *.msl file in my metasprites build process that is a python script separate from NESAC.

Labels can be saved and loaded separately (*.msl) but are also stored in the binary along with the animation sequences, so theoretically the label files are not needed apart from providing the ability to easily edit and parse them as they are actually just text files.

Here's an example of exported assembly animation data:
Code:
playersprite1_animation_data:
   @last_animation_index:   .db 5

   .dw @running
   .dw @dying
   .dw @idle
   .dw @hit
   .dw @jumping
   .dw @throwing

   @running:
      .db 12   ; index of last frame starting from this byte.
      .db 0   ; index of frame to loop to * 3. If playersprite1_run_01 is 0 and playersprite1_run_02 is 1, then those would be 0 and 3.

      .db 4   ; time delay for this frame.
      .dw playersprite1_run_01   ; metasprite data pointer for this frame.

      .db 4   ; time delay for this frame.
      .dw playersprite1_run_02   ; metasprite data pointer for this frame.

      .db 4   ; time delay for this frame.
      .dw playersprite1_run_01   ; metasprite data pointer for this frame.

      .db 4   ; time delay for this frame.
      .dw playersprite1_run_03   ; metasprite data pointer for this frame.


   @dying:
      .db 18
      .db 15

      .db 10
      .dw playersprite1_dying_01

      .db 10
      .dw playersprite1_dying_02

      .db 5
      .dw playersprite1_dying_01

      .db 10
      .dw playersprite1_dying_02

      .db 5
      .dw playersprite1_dying_03

      .db 35
      .dw playersprite1_dying_04


   @idle:
      .db 3
      .db 0

      .db 4
      .dw playersprite1_idle


   @hit:
      .db 3
      .db 0

      .db 4
      .dw playersprite1_hit_01


   @jumping:
      .db 3
      .db 0

      .db 4
      .dw playersprite1_run_03


   @throwing:
      .db 6
      .db 3

      .db 4
      .dw playersprite1_throw_01

      .db 4
      .dw playersprite1_throw_02



The exported data is read by my game engine in such a way that a value is used to index an animation from the top list. The proper frame pointer is then collected from the indexed animation using a variable storing the current frame.
Again, it is probably best to either edit the source of the assembly export or create yourself a script (python/whatever) to parse the binary data of the animations to fit the needs and style of your engine.

As you can probably tell, I've shamelessly copied layout aspects from NESST by Shiru. My goal was not to impress anyone with my own design skills, I just wanted something functional and since NESST is so widely used I figured I might just make something that looks familiar. At first I wanted to attempt adding this as a feature to NESST itself as a fork, but I do not have the required Borland compiler and my C++ skills are minimal anyway. I am much more familiar with C#.

I will be making updates later on to add the ability to reorganize the order of animations and animation cels/frames. Currently only 8x8 mode is supported as I have no experience using 8x16 on the NES yet, but from what I understand it should be a quick feature to add. No support for banking or CHR-RAM either, as that is not something I feel I grasp enough yet nor have a test case for.

Thank you all for the insight into your ways of handling animation data! I hope this tool can prove to be useful to someone more than just myself. :)

:beer:
Re: Ways of implementing sprite animations
by on (#237781)
Add animation, add frame. Click play. Add animation, click the new animation in the list. Unhandled exception.

Open program, add animation, export asm. Type any filename. Unhandled exception.

I actually cannot find a way to export asm without crashing, but perhaps something much earlier I did breaks it in a way that survives boots? For example, even these steps create an unhandled exception for me:
1. Open Program
2. Metasprites, open .msp
3. Select a valid msp file
4. Tile, open CHR
5. Select a valid chr file.
6. Click Add (animation) twice.
7. Click Add (frame).
8. Click animate_1
9. Click Add (frame).
10. Tools, export animations as .asm.

I don't have a guess as to why. The program can save a labels file as the exact same name as I am trying to save the .asm as, so it's not that saving is generally broken for some reason.

Edit: I think I narrowed it down. If the first animation (animation_0) has exactly one frame, the error occurs for me. The second animation (animation_1) can have one frame, but not the first.

I request that adding a frame (or animation) selects that frame (or animation) in the list. Clicking add, then delete does not delete the newly added frame, which is probably the usual want.

I'd also request the keyboard way NESST uses to switch metasprites (The [ and ] keys.)
Re: Ways of implementing sprite animations
by on (#237783)
Thanks for the feedback! :) I obviously had not tested the software well enough...

I should be able to fix those issues soon and I'll try to test things a bit more thorough next time. I'll get those NESST shortcut keys in as well and the behavior you described of having new frames being selected by default.

Cheers!

EDIT: I forgot to mention the shortcut CTRL+SPACE to toggle playback. There will be more to come!
Re: Ways of implementing sprite animations
by on (#237791)
OK, here's an update with fixes for the bugs you experienced Kasumi.
Attachment:
NESAC.zip [17.69 KiB]
Downloaded 117 times

I've also added the shortcut keys you requested, as well as two new ones - CTRL+LEFT / CTRL+RIGHT to switch focus between animation and frame lists.
Another thing I did was change the default metasprite labels to be derived from the *.msb file name, rather than just "metasprite_X". I thought this would decrease the risk of having duplicate labels between metasprite banks.
Re: Ways of implementing sprite animations
by on (#237806)
If exporting to C like Nesst is not on the list, to export to a generic format like Json so we can parse it to our own format would be nice. Will see if I can try it soon.
Re: Ways of implementing sprite animations
by on (#237815)
Both of those features sound like good additions Banshaku. I'll add them later tonight. :)
Re: Ways of implementing sprite animations
by on (#237834)
I think I need some help on the formatting for JSON and C export.

Would this be correct JSON?:
Code:
var player1_metasprites = {[
   "playersprite1_run_01":[
      { "x":17, "y":24, "tile":91, "attr":3 },
      { "x":20, "y":24, "tile":92, "attr":3 },
      ...
   ],
   "playersprite1_run_02":[
      { "x":17, "y":24, "tile":91, "attr":3 },
      { "x":20, "y":24, "tile":92, "attr":3 },
      ...
   ],
   ...
]}

var player1_animations = {[
   "running":{
      "loop_frame":0,
      "frames":[
         { "delay":4, "frame":"playersprite1_run_01" },
         { "delay":4, "frame":"playersprite1_run_02" },
         { "delay":4, "frame":"playersprite1_run_01" },
         { "delay":4, "frame":"playersprite1_run_03" }
      ]   
   }
]}


As for C, which I am even less accustomed to, does this look somewhat right?:
Code:
// First the metasprites:
const unsigned char playersprite1_run_01[]={

    17, 24,0x5b,3,
    20, 24,0x5c,3,
    22, 32,0x6b,3,
    16, 32,0x6a,3,
    15, 48,0x40,3,
    23, 48,0x41,3,
    12, 23,0x3e,1,
    20, 23,0x3f,1,
    14, 32,0x01,2,
    27, 32,0x02,2,
    12, 40,0x10,2,
    20, 40,0x11,2,
    27, 40,0x12,2,
   128

// Followed by the rest of the metasprites...
// .......
// .......

// Then the animations:
const unsigned char @running[]={
   0,   // index of frame to loop to.
   4,   // time delay for this frame.
   playersprite1_run_01,   // metasprite data pointer for this frame.
   4,   // time delay for this frame.
   playersprite1_run_02,   // metasprite data pointer for this frame.
   4,   // time delay for this frame.
   playersprite1_run_01,   // metasprite data pointer for this frame.
   4,   // time delay for this frame.
   playersprite1_run_03   // metasprite data pointer for this frame.
}

const unsigned char @idle[]={
   0,
   4,
   playersprite1_idle
}


// Finally, the list of pointers to the animations:
const unsigned char* const player1_animations[]={
   @running,
   @idle
};

Apart from the "@" chars on the labels, I'm guessing I'm not storing the pointers to the metasprites correctly in the animations. They would have to be stored as int, right? Like &label_name? But then they would also not be able to be put in a char array. Is there any way to neatly cast the pointer of the metasprite to the upper and lower bytes of an int? So that they would be stored like this:
Code:
const unsigned char @idle[]={
   0,
   4,
   <&playersprite1_idle,      // this would be the lower byte of the label.
   >&playersprite1_idle      // this would be the upper.
}


Any help is appreciated!
Re: Ways of implementing sprite animations
by on (#237890)
pwnskar wrote:
Would this be correct JSON?
Not quite, for a couple reasons.

The first is that {["foo": 1, "bar": 2]} is bad syntax. It should be {"foo": 1, "bar": 2}; square brackets are used for lists like [1, 2, 3].

The second is that JSON should just be a single expression. Stuff like var foo = ... isn't allowed; if you use it, you're writing JavaScript, not JSON.

So, this is bad:
Code:
var player1_metasprites = {...}
var player1_animations = {...}
And this is good:
Code:
{
    "player1_metasprites": {...},
    "player1_animations": {...}
}
For more information, click here.

pwnskar wrote:
As for C, which I am even less accustomed to, does this look somewhat right?
Besides @ being invalid in variable names, and a few missing semicolons, it looks reasonable enough.

pwnskar wrote:
Is there any way to neatly cast the pointer of the metasprite to the upper and lower bytes of an int?
Like this (assuming playersprite1_idle is a pointer):
Code:
    ((unsigned int) playersprite1_idle) & 0xff,      // this would be the lower byte of the label.
    ((unsigned int) playersprite1_idle) >> 8         // this would be the upper.
You may want to define macros just for convenience's sake.
Re: Ways of implementing sprite animations
by on (#237901)
Nicole wrote:
pwnskar wrote:
Would this be correct JSON?
Not quite, for a couple reasons.

The first is that {["foo": 1, "bar": 2]} is bad syntax. It should be {"foo": 1, "bar": 2}; square brackets are used for lists like [1, 2, 3].
Thanks for the reply! :) My reasoning for using [] was to contain a list of metasprites, each containing a list of OAM entries, so I'm guessing I did that part right? Or am I still missing something?

Nicole wrote:
pwnskar wrote:
Is there any way to neatly cast the pointer of the metasprite to the upper and lower bytes of an int?
Like this (assuming playersprite1_idle is a pointer):
Code:
    ((unsigned int) playersprite1_idle) & 0xff,      // this would be the lower byte of the label.
    ((unsigned int) playersprite1_idle) >> 8         // this would be the upper.
You may want to define macros just for convenience's sake.
Yes, they are supposed to be pointers to the previously declared metasprites. I'm not very familiar with using macros but would I be correct to assume that they cannot/should not be defined more than once? Would it then be unfavorable to put a macro together with each exported C animation list or would it rather be a good tradeoff to get a more human readable output?

Cheers!
Re: Ways of implementing sprite animations
by on (#237965)
pwnskar wrote:
Thanks for the reply! :) My reasoning for using [] was to contain a list of metasprites, each containing a list of OAM entries, so I'm guessing I did that part right? Or am I still missing something?
These are correct:
Code:
[value1, value2, value3]
{"key1": value1, "key2": value2, "key3": value3}
And you can nest them freely:
Code:
{"key1": [1, 2, 3], "key2": [4, 5]}
[{"foo": 10, "bar": 20}, {"foo": 15, "bar": 22}]
But you have something like this in your code, which isn't right:
Code:
{["key1": value1, "key2": value2, "key3": value3]}

Nicole wrote:
Yes, they are supposed to be pointers to the previously declared metasprites. I'm not very familiar with using macros but would I be correct to assume that they cannot/should not be defined more than once? Would it then be unfavorable to put a macro together with each exported C animation list or would it rather be a good tradeoff to get a more human readable output?
Well, you could put something like this near the top, and then the macros would only be defined once. Not sure if it's best practice though.
Code:
/* FOO should be some prefix unlikely to conflict with anything else */
#ifndef FOO_MACROS
#define FOO_MACROS
#define FOO_LOBYTE(p) (((unsigned int) (p)) & 0xff)
#define FOO_HIBYTE(p) (((unsigned int) (p)) >> 8)
#endif
Re: Ways of implementing sprite animations
by on (#237983)
Thank you for your continued help, Nicole!

Here's a slimmed down version of what the JSON export looks like right now:
Code:
{
   "metasprites": [
      {
         "label": "playersprite1_idle",
         "oam_entries": [
            {
               "x": "57",
               "y": "32",
               "tile": "91",
               "attributes": "3",
            },
            {
               "x": "60",
               "y": "32",
               "tile": "92",
               "attributes": "3",
            }
         ]
      },
      {
         "label": "playersprite1_run_03",
         "oam_entries": [
            {
               "x": "57",
               "y": "30",
               "tile": "91",
               "attributes": "3",
            },
            {
               "x": "60",
               "y": "30",
               "tile": "92",
               "attributes": "3",
            }
         ]
      },
      {
         "label": "playersprite1_run_01",
         "oam_entries": [
            {
               "x": "57",
               "y": "32",
               "tile": "91",
               "attributes": "3",
            },
            {
               "x": "60",
               "y": "32",
               "tile": "92",
               "attributes": "3",
            }
         ]
      },
      {
         "label": "playersprite1_run_02",
         "oam_entries": [
            {
               "x": "56",
               "y": "38",
               "tile": "6",
               "attributes": "2",
            },
            {
               "x": "56",
               "y": "46",
               "tile": "22",
               "attributes": "2",
            }
         ]
      }
   ],
   "animations": [
      {
         "label": "@running",
         "loop_frame": "0",
         "frames": [
            {
               "delay": "4",
               "metasprite": "playersprite1_run_01"
            },
            {
               "delay": "4",
               "metasprite": "playersprite1_run_02"
            },
            {
               "delay": "4",
               "metasprite": "playersprite1_run_01"
            },
            {
               "delay": "4",
               "metasprite": "playersprite1_run_03"
            }
         ]
      },
      {
         "label": "@idle",
         "loop_frame": "0",
         "frames": [
            {
               "delay": "4",
               "metasprite": "playersprite1_idle"
            }
         ]
      }
   ]
}

Does that look valid or am I still doing something wrong? I've run it through an online JSON to XML converter and it seems to pass without errors.

I've still not done any work on a C exporter but it will come. While I'm at it I'm adding support for 8x16 along with the ability to load in two pattern tables to support that. Another idea I have is to add the ability to store any data you wish on each animation frame as a string. So if you for example wish to be able to store opcodes for use with your own game engine you could store those there and have them converted to your own format by making your own script to handle that when parsing the exported animations.

Any feedback or ideas on more features are welcome.

Cheers! :beer:
Re: Ways of implementing sprite animations
by on (#237987)
Since I'm the one that mentioned JSON, I should maybe give more feedback about it ^^;;;

Well, you can define OAM entries like you did, which is one way and there is no confusion about what is what but it's more verbose. Another way, like the nesst version of C data is to just define the order of the OAM data and put it in an array like this:

(oam entry part only)
Code:
{
    oam_entries : [
     0x57, 0x32, 0x91, 0x03,
     0x60, 0x32, 0x92, 0x03
    ]
}


This one is less clear but once you know the order, is not harder to parse. There is no "perfect" way, it mostly a matter of preference actually. So for now you current format is verbose but won't be error prone ;)
Re: Ways of implementing sprite animations
by on (#237992)
Banshaku wrote:
Since I'm the one that mentioned JSON, I should maybe give more feedback about it ^^;;;

Thanks for the feedback! I agree with the format you suggested, as it makes the output smaller and IMO more human readable as long as you understand what the four values on each row represent. Also, since it better matches the C output of NESST, it might be more familiar to most users and therefore require less learning.

Thanks! :)
Re: Ways of implementing sprite animations
by on (#238009)
The only problem is that the JSON standard only allows decimal numbers, so hexadecimal numbers are off-limits.
Re: Ways of implementing sprite animations
by on (#238011)
@Nicole

I kind of had a feeling that you couln't but wrote it anyway. That's what happens when you have to juggle with so many format/language, you tend to forget ^^;;;

Since the goal is to parse the content into your own format, either we save each value as string that can be formatted for either C/ASM ("$00" or "0x00") or just write decimal values and you just format that decimal in the target format you want later, which is maybe the better choice after all (they are often formater form decimal to -> xxx in many language anyway so it's easier than parsing a string).
Re: Ways of implementing sprite animations
by on (#238029)
Banshaku wrote:
Since the goal is to parse the content into your own format ....just write decimal values and you just format that decimal in the target format you want later, which is maybe the better choice after all (they are often formater form decimal to -> xxx in many language anyway so it's easier than parsing a string).


I'm a fan of this method. If a tool will output data into a very standard format (JSON, XML, YAML, CSV, I don't care), then it makes it very easy to write a script to convert it to whatever crazy format I need for my engine. If I need the values written out as hex, I'm happy to convert them myself.
Re: Ways of implementing sprite animations
by on (#238033)
gauauu wrote:
Banshaku wrote:
Since the goal is to parse the content into your own format ....just write decimal values and you just format that decimal in the target format you want later, which is maybe the better choice after all (they are often formater form decimal to -> xxx in many language anyway so it's easier than parsing a string).


I'm a fan of this method. If a tool will output data into a very standard format (JSON, XML, YAML, CSV, I don't care), then it makes it very easy to write a script to convert it to whatever crazy format I need for my engine. If I need the values written out as hex, I'm happy to convert them myself.

Yes, I agree as well.

Here's what the JSON looks like atm:
Code:
{
   "metasprites": [
      {
         "label": "playersprite1_idle",
         "oam_entries": [
            57, 32, 91, 3,
            60, 32, 92, 3,
            62, 40, 107, 3,
            56, 40, 106, 3,
            55, 56, 64, 3,
            63, 56, 65, 3,
            52, 31, 62, 1,
            60, 31, 63, 1,
            54, 40, 1, 2,
            67, 40, 2, 2,
            52, 48, 16, 2,
            60, 48, 17, 2,
            67, 48, 18, 2
         ]
      },
      {
         "label": "playersprite1_run_03",
         "oam_entries": [
            57, 30, 91, 3,
            60, 30, 92, 3,
            62, 38, 107, 3,
            56, 38, 106, 3,
            50, 53, 72, 3,
            58, 53, 73, 3,
            66, 52, 70, 3,
            52, 29, 62, 1,
            60, 29, 63, 1,
            51, 38, 8, 2,
            66, 38, 10, 2,
            50, 46, 24, 2,
            58, 45, 25, 2,
            66, 46, 26, 2
         ]
      },
      {
         "label": "playersprite1_run_01",
         "oam_entries": [
            57, 32, 91, 3,
            60, 32, 92, 3,
            62, 40, 107, 3,
            56, 40, 106, 3,
            52, 56, 66, 3,
            60, 56, 67, 3,
            52, 31, 62, 1,
            59, 29, 89, 129,
            52, 40, 3, 2,
            52, 48, 19, 2,
            60, 48, 20, 2,
            68, 48, 21, 2
         ]
      },
      {
         "label": "playersprite1_run_02",
         "oam_entries": [
            56, 38, 6, 2,
            56, 46, 22, 2,
            64, 46, 23, 2,
            57, 30, 91, 3,
            60, 30, 92, 3,
            62, 38, 107, 3,
            56, 38, 106, 3,
            52, 52, 68, 3,
            60, 52, 69, 3,
            68, 52, 70, 3,
            52, 29, 62, 1,
            60, 29, 63, 1
         ]
      },
      {
         "label": "playersprite1_holding_weapon_run_01",
         "oam_entries": [
            49, 30, 62, 1,
            55, 28, 89, 129,
            54, 31, 94, 3,
            62, 31, 95, 3,
            54, 39, 110, 3,
            62, 39, 111, 3,
            52, 56, 66, 3,
            60, 56, 67, 3,
            46, 32, 11, 2,
            46, 40, 27, 2,
            54, 40, 12, 2,
            62, 40, 13, 2,
            70, 38, 14, 2,
            54, 48, 28, 2,
            62, 48, 29, 2
         ]
      },
      {
         "label": "playersprite1_holding_weapon_run_02",
         "oam_entries": [
            49, 29, 62, 1,
            57, 29, 63, 1,
            54, 30, 94, 3,
            62, 30, 95, 3,
            54, 38, 110, 3,
            62, 38, 111, 3,
            52, 52, 68, 3,
            60, 52, 69, 3,
            68, 52, 70, 3,
            46, 31, 11, 2,
            46, 39, 27, 2,
            54, 39, 12, 2,
            62, 38, 13, 2,
            70, 37, 14, 2,
            54, 46, 28, 2,
            62, 46, 29, 2
         ]
      },
      {
         "label": "playersprite1_holding_weapon_run_03",
         "oam_entries": [
            49, 29, 62, 1,
            57, 29, 63, 1,
            54, 30, 94, 3,
            62, 30, 95, 3,
            54, 38, 110, 3,
            62, 38, 111, 3,
            58, 52, 73, 3,
            50, 52, 72, 3,
            66, 51, 70, 3,
            46, 31, 11, 2,
            46, 38, 27, 2,
            54, 39, 12, 2,
            62, 39, 13, 2,
            70, 37, 14, 2,
            54, 46, 28, 2,
            62, 46, 29, 2
         ]
      }
   ],
   "animations": [
      {
         "label": "running",
         "loop_frame": 0,
         "frames": [
            {
               "delay": 4,
               "metasprite": "playersprite1_run_01"
            },
            {
               "delay": 4,
               "metasprite": "playersprite1_run_02"
            },
            {
               "delay": 4,
               "metasprite": "playersprite1_run_01"
            },
            {
               "delay": 4,
               "metasprite": "playersprite1_run_03"
            }
         ]
      },
      {
         "label": "idle",
         "loop_frame": 0,
         "frames": [
            {
               "delay": 4,
               "metasprite": "playersprite1_idle"
            }
         ]
      }
   ]
}


And here's where the C export is at:
Code:
#ifndef NESAC_MACROS
#define NESAC_MACROS
#define NESAC_LOBYTE(p) (((unsigned int) (p)) & 0xff)
#define NESAC_HIBYTE(p) (((unsigned int) (p)) >> 8)
#endif

const unsigned char playersprite1_idle[]={
   57, 32, 0x5b, 3,
   60, 32, 0x5c, 3,
   62, 40, 0x6b, 3,
   56, 40, 0x6a, 3,
   55, 56, 0x40, 3,
   63, 56, 0x41, 3,
   52, 31, 0x3e, 1,
   60, 31, 0x3f, 1,
   54, 40, 0x01, 2,
   67, 40, 0x02, 2,
   52, 48, 0x10, 2,
   60, 48, 0x11, 2,
   67, 48, 0x12, 2,
   128
};

const unsigned char playersprite1_run_03[]={
   57, 30, 0x5b, 3,
   60, 30, 0x5c, 3,
   62, 38, 0x6b, 3,
   56, 38, 0x6a, 3,
   50, 53, 0x48, 3,
   58, 53, 0x49, 3,
   66, 52, 0x46, 3,
   52, 29, 0x3e, 1,
   60, 29, 0x3f, 1,
   51, 38, 0x08, 2,
   66, 38, 0x0a, 2,
   50, 46, 0x18, 2,
   58, 45, 0x19, 2,
   66, 46, 0x1a, 2,
   128
};

const unsigned char playersprite1_run_01[]={
   57, 32, 0x5b, 3,
   60, 32, 0x5c, 3,
   62, 40, 0x6b, 3,
   56, 40, 0x6a, 3,
   52, 56, 0x42, 3,
   60, 56, 0x43, 3,
   52, 31, 0x3e, 1,
   59, 29, 0x59, 129,
   52, 40, 0x03, 2,
   52, 48, 0x13, 2,
   60, 48, 0x14, 2,
   68, 48, 0x15, 2,
   128
};

const unsigned char playersprite1_run_02[]={
   56, 38, 0x06, 2,
   56, 46, 0x16, 2,
   64, 46, 0x17, 2,
   57, 30, 0x5b, 3,
   60, 30, 0x5c, 3,
   62, 38, 0x6b, 3,
   56, 38, 0x6a, 3,
   52, 52, 0x44, 3,
   60, 52, 0x45, 3,
   68, 52, 0x46, 3,
   52, 29, 0x3e, 1,
   60, 29, 0x3f, 1,
   128
};

const unsigned char* const test_metasprite_list[]={
   playersprite1_idle,
   playersprite1_run_03,
   playersprite1_run_01,
   playersprite1_run_02
};

const unsigned char running[]={
   0,   // index of frame to loop to.
   4,   // time delay for this frame.
   NESAC_LOBYTE(&playersprite1_run_01),
   NESAC_HIBYTE(&playersprite1_run_01),
   4,   // time delay for this frame.
   NESAC_LOBYTE(&playersprite1_run_02),
   NESAC_HIBYTE(&playersprite1_run_02),
   4,   // time delay for this frame.
   NESAC_LOBYTE(&playersprite1_run_01),
   NESAC_HIBYTE(&playersprite1_run_01),
   4,   // time delay for this frame.
   NESAC_LOBYTE(&playersprite1_run_03),
   NESAC_HIBYTE(&playersprite1_run_03)
};

const unsigned char idle[]={
   0,   // index of frame to loop to.
   4,   // time delay for this frame.
   NESAC_LOBYTE(&playersprite1_idle),
   NESAC_HIBYTE(&playersprite1_idle)
};

const unsigned char* const test[]={
   NESAC_LOBYTE(&running),
   NESAC_HIBYTE(&running),
   NESAC_LOBYTE(&idle),
   NESAC_HIBYTE(&idle)
};


So as you can see, I've tried to copy what NESST does but split up the metasprite data in separate arrays. I've still kept the list of pointers though.. don't know if that would come of any use to someone though, so maybe I should just cut it?
But idk, I mean I did add such a list for the animations as well and I'm actually using it (in asm form) in my own project. In the end I guess most people would favor the JSON export though, since it's easier to parse and do whatever you want with.

I've only tried compiling the C export for x86 but from what I can tell the macros seem to work as Nicole intended. One thing that I've cut out from the pasted code is all the unused metasprites. Unused as in not being part of an animation. I'm not sure whether I should check that each metasprite is used or just leave it up to the user. My thinking is there might be a case where you will need some animation that would need to be coded by hand (i have such animations myself) and in such a case you could still draw benefit from having those metasprites exported with their labels. What do you guys think?

:beer:

EDIT: I just discovered that I don't store the metasprite offset from NESST anywhere in the tool. I'll add that to all of the exports ASAP.
Re: Ways of implementing sprite animations
by on (#238353)
I've updated NESAC:
Attachment:
NESAC.zip [20.5 KiB]
Downloaded 117 times

Added features:
+ JSON and C export for animations and metasprites.
+ Ability to load in two tilesets.
+ 8x16 mode for metasprites

For another update I think it might be a good idea to add the ability to export assembly and C with or without metasprites bundled in the same file. Right now it's inconsistent with assembly just outputting animations and C outputting both. I think it would be a good option to let users chose to export them in parts. One such case would be if you're pleased with how the animations are formatted but want to do some RLE packing on your metasprites. Then you could export the animations in asm/C and do whatever you want with the metasprites by parsing the JSON export.

Please let me know if you have any ideas or suggestions on this or anything else!

Cheers! :beer:
Re: Ways of implementing sprite animations
by on (#238370)
One feature you can add later but is complicated to implement and specific to complicated animation is to allow to change 1k/2k/4k tileset per frame. It may sound unusual but for my current project, when you have boss with complicated frame that are stored in 1k bank slot, you often have to change bank on every frame. This make it quite a pain to test the created sprites set in nesst since you have to manually load the tiles when you change frame.

No rush to add that option but it's an interesting feature when you frames becomes complicated.
Re: Ways of implementing sprite animations
by on (#238372)
That sounds like a good feature. I've not yet tried implementing something like that on the NES myself but I think it should be relatively easy for me to add as a feature in NESAC by adding the ability to load multiple banks and set/read a bank switch command for each animation frame.

Some questions arise, though:

What should be the maximum amount of banks?
Should there be a maximum amount, even?
How should the export for the C and assembly versions of this feature look?

My first thought about number of banks is that they would be limited to a set number but idk what the biggest mapper available today can hold. I'm guessing 256 is a nice big number.
I could perhaps add a listbox of banks somewhere nicely in the gui. Perhaps in an expandable panel that's minimized by default.

About the last question on exports, I'm happy to take any suggestions on how to format the bank switching instructions for C and assembly. How do you store that data in your project, Banshaku?

Thanks for the feedback!
Re: Ways of implementing sprite animations
by on (#238383)
One game I've been involved with uses an array containing a pointer to each cel's metasprite definition and a second array containing which CHR bank each cel's tiles are in.
Re: Ways of implementing sprite animations
by on (#238385)
Nicole wrote:
The only problem is that the JSON standard only allows decimal numbers, so hexadecimal numbers are off-limits.


Many aspects of the design of Javascript are really terrible, but one very useful aspect of that language is that almost any modern system will have an implementation available which can be used to run many kinds of code by by wrapping the code in some HTML, saving it somewhere with a `.html` extension, and the loading it as a web page which can then place output in a text field. If one a JSON-style object is written to a file in the same directory as such a "web page" preceded by something like "var someName = {objectGoesHere};", the web page will be able to use a "<script src='filename'></script>" tag to read the data in question even if the data includes hex numbers, constants, or other things that aren't allowed in a JSON file.

Note that code running in a web page will, unless the browser is buggy, be limited in what it is allowed to do. While data set by Javascript loaded via "<script>" tag that accesses something in the same directory can be accessed without restriction, scripts will have a limited ability to process graphics fetched for an "<img>" tag unless the path to the IMG has been set using a file-selector or URL-selector object. While having to manually select files is less convenient than having a page simply use them directly, it also makes browsers much safer as script-running tools.

To generate the tile data for Ruby Runner, I write some Javascript, stuck it in a web page, and had it produce a couple of text objects whose contents could then be copied and pasted into include files for ca65; such an approach will allow the scripts to be processed by any system without being bound to Linux, Macintosh, or Windows.
Re: Ways of implementing sprite animations
by on (#238386)
For now I'm still in the design phase on how data should be managed but it is basically like tepples said: one array contains a list of metasprite and an array of char contains which bank to switch.

As for maximum size, well, a long time ago I was trying to make an editor and trying to define all the maximum that could be used for each in mapper. Now in hindsight, adding all those complication which are just artificial limitations doesn't mean it will make the app better: since the user should know what he's/she's is doing, if I define 20000k of data for a mapper that doesn't exist then it won't be usable. Many programs often put limitations and later those limitation gets in the way.

The only real issue is "how" to define the banks and metasprite data since it affect the indexes of the tiles. For example, when using 1k banks, an engine will decide automatically in which slot the metasprite will be loaded. This means the indexes of tiles will need to be adapted for that slot. In the editor, what is the better way to represent it then: always from slot 0 (first 1k)? Allow any slot? It depends how the game engine manage the slots so this part would be tricky and may be necessary to be flexible enough.
Re: Ways of implementing sprite animations
by on (#238388)
My metasprite routine represents a cel as always in sprite slot 0 (CHR $1000-$13FF). Tile numbers in a metasprite are $00 through $3F, relative to the start of the slot, freeing bits 7 and 6 of the tile number byte to tell whether each individual sprite is flipped vertically or horizontally.

To this tile number the metasprite drawing routine adds the value of the "metasprite base tile" parameter. For games using 1K CHR bank switching (MMC3, VRC2/4, FME-7), this starting value is $00, $40, $80, or $C0, depending on which of the four windows was chosen for a particular actor. For games that stream sprite cels into CHR RAM, this value would be set to the start of the first or second half of a particular actor's sprite cel buffer. I've developed games that use both strategies.*

During a game, the "metasprite base tile" parameter is derived from a property of each actor. In the 1K switching game, sprite sheets can be "small", that is, one 1 KiB or 64-tile page, or "big", comprising multiple pages. Different actors can have the same window value if they use the same small sprite sheet, such as powerups or multiple instances of a simple enemy or even a pair of simple enemies that commonly occur together. But different actors using a big sprite sheet (usually the player character or bosses) must use separate windows. In the game that uses streaming, I hardcoded each actor's space as two 16-tile windows, but if I had to do it over again, I'd probably store the size in tiles of the largest cel along with the sprite sheet and use that to set the size of each actor's space.

Fortunately, during the development of this game, I negotiated permission to reuse low-level code not deemed game-specific in public projects under a free software license. I haven't released everything yet, but I ended up reusing the metasprite code in two projects under the zlib License:

  • From the test harness for a port of the Pently audio driver to ASM6: metasprite.asm
  • From a port of nocash's Magic Floor to Game Boy: metasprite.z80

This released code does not include nontrivial calculation of "metasprite base tile" because I haven't yet made any solo game projects complex enough to need a 1K-switching mapper or sprite cel streaming.


Being careful
Re: Ways of implementing sprite animations
by on (#238393)
I think I'm roughly following what both of you are saying, Bankashu and tepples.

Focusing on keeping the tool ROM based without adding the complexity of RAM based animation, I think it should be possible for me to implement what tepples describes about storing an offset for the tile entries.

It makes sense to me to store the information of which bank to load in which slot along with the metasprite rather than the animation data. That way the metasprite would be correctly displayed when browsing the metasprites and not just during playback. I guess there would be instances where you have a character that actually re-uses the same metasprite data only with switched CHR banks (I think Solstice might be doing this but with CHR-RAM instead of banks). But for the sake of keeping down the complexity of the tool I think it's probably best to start with just having the bank switch data stored with the metasprites, as I'm guessing CHR-ROM is what most devs go for.

I'll have a go at it tonight and see how far I get.

Cheers!