{
  "path": "/posts/dead-simple-spritesheet-animation",
  "site": "at://did:plc:pans3xjam4khj7y54dx7gtfg/site.standard.publication/3mdqevmg6w32c",
  "tags": [
    "rust",
    "game dev",
    "animation",
    "cryptoadz",
    "WASM"
  ],
  "$type": "site.standard.document",
  "title": "Dead Simple Spritesheet Animation",
  "description": "A spritesheet animation state machine in Rust with page-based looping and variant transitions for 2D games.",
  "publishedAt": "2022-12-05T06:57:16.000Z",
  "textContent": "Live | Repository\n\nMotivation\n\nI’ve been using Bevy for a video game project and it’s been a delightful experience. As the project has grown, I’ve turned into a level designer, animator, illustrator, along with being a programmer.\n\nSometimes I hit walls, and rabbit holes around those walls are too compelling to pass up. One such rabbit hole was building an animation state machine that could create different looping animations from a single sprite sheet. The only reason for using a single sprite-sheet is that being the dev and the illustrator is very time consuming and I wanted my workflow to remain as simple as possible. But as I've built a few different prototypes the single spritesheet model has proven to be quite useful.\n\n!Sprite sheet\n\n!Animated\n\nI created an animation-transition crate that abstracts most of the details away and I've used it on multiple prototypes so I think that it is fairly straight forward to use in 2D games.\nThe following is a sort of how-to guide to create an animation state machine from scratch.\n\nAnimation Pages\n\nI first create an enum with variants with self explanatory names. For the above example three variants suffice: Idle, Rising, and Falling.\nThe enum variants are then mapped to animation pages.\nPages are a way to encode the information needed to build looping animations. They are made up of two parts: an offset, and a page size.\nThe offset stores the index of the first sprite in a single animation loop, and page size stores the number of sprites in the same animation.\n\nImplementation\n\nLet's take a look at some code.\n\nAnimation variants enum\n\nThe enum needs to implement PartialEq so that the variants can be compared to each other when making decisions during animation state transitions.\n\nCreating an AnimationLoop trait\n\nThe trait is straightforward, defines a function that would return the offset and size of the animation page.\n\nImplementing the AnimationLoop trait for the enum\n\nThis is also straightforward, I match the enum variant to the appropriate animation page tuple i.e. frame offset and page size.\n\n---\n\nState Transitions\n\nPlaying a single looping animation can be accomplished by flipping through a contiguous array of indices, that are then used to fetch the appropriate sprite from a sprite atlas.\nMoving between different animations can be accomplished by simply updating the animation variant.\n\n---\n\nImplementation\n\nI encapsulate the information needed for the two actions (looping animation, and variant transition) in a struct called PlayerAnimationState.\n\nAnimation state manager\n\nThe struct stores the animation variant to play, and the current index of the frame.\n\nImplementing transition functions\n\nwrapping_next_idx increments the current index and wraps around at the page boundary.\nIt makes looping animations trivial to implement.\n\ntransition_variant updates the animation state manager so that I can play a different animation based on in-game events.\nThis one accepts an argument of type PlayerAnimationVariant, so different in-game events can trigger appropriate animations in response. For example when I press spacebar on Toad, the animation state is first updated to Rising, once the toad reaches it's maximum jump height the animation is updated to Falling, and finally on touching the ground it can be transitioned back to Idle.\n\n---\nAll of this can be somewhat tedious, and repetitive if you're working on multiple games. I created a Rust crate that simplifies the process by providing:\n- the traits AnimationLoop and AnimationTransition<T: AnimationLoop>\n- a handy proc macro AnimationTransitionMacro that implements the necessary index/variant manipulation functions for the animation state manager",
  "canonicalUrl": "https://afloat.boats/posts/dead-simple-spritesheet-animation"
}