Announcing anitomata: Composable 2D sprite animation in Haskell
anitomata
is a pure implementation of 2D sprite animation intended for use in
gamedev. In this example, anim
is an animation for an NPC celebrating a
victory. The animation sequence plays the NPC’s idle
animation two times then
the jump
animation one time, and the entire sequence is looped indefinitely:
import Anitomata
import qualified Data.Vector.Unboxed as U
anim :: Anim
=
anim AnimDurationDefault
buildAnim $ repeatAnim AnimRepeatForever
$ repeatAnim (AnimRepeatCount 1) idle <> jump
idle :: AnimBuilder
= fromAnimSlice idleSlice
idle
jump :: AnimBuilder
= fromAnimSlice jumpSlice
jump
idleSlice :: AnimSlice
=
idleSlice AnimSlice
= AnimDirBackward
{ animSliceDir = U.replicate 4 0.1 -- Each frame is 100ms
, animSliceFrameDurs = U.fromListN 4 [{- ... AnimFrame values ... -}]
, animSliceFrames
}
jumpSlice :: AnimSlice
=
jumpSlice AnimSlice
= AnimDirForward
{ animSliceDir -- Second frame is 500ms, rest are 100ms
= U.generate 8 $ \i -> if i == 1 then 0.5 else 0.1
, animSliceFrameDurs = U.fromListN 8 [{- ... AnimFrame values ... -}]
, animSliceFrames }
AnimSlice
is the smallest building block of an animation. Slices are a minimal
sequence of frames that capture a logical chunk of animation. Slices are
converted to AnimBuilder
values and then the builders can be combined using
the Semigroup
interface. Values of the core animation type - Anim
- are
created from builders.
A game can play an animation by stepping it using stepAnim
each simulation
frame, passing the time elapsed since the last step:
stepAnim :: Double -> Anim -> SteppedAnim
data SteppedAnim = SteppedAnim
steppedAnimStatus :: AnimStatus
{ steppedAnimValue :: Anim
, }
An animation can be rendered using animFrame
in conjunction with a spritesheet
that is managed separately by the game. animFrame
provides the current frame
of the animation:
animFrame :: Anim -> AnimFrame
Note that the types in the library are more general than what is shown above.
For example, there is no requirement of using Double
as a duration type,
unboxed Vector
as the vector type, etc.
The animation building blocks can be defined manually, but this is tedious and
error-prone. Instead, the base slices and builders are typically defined
automatically by feeding a design file - e.g. output from Aseprite - into a code
generator or parsing some translated representation of a design file. Packages
providing this functionality may be found by visiting the project’s
homepage or by searching Hackage (all
official packages of the anitomata
project are named anitomata-*
).
For additional info, please see the following resources:
- Project homepage
- Haddocks
- Supplemental announcement video
- Demonstrates
anitomata
concepts using an interactive testbed application
- Demonstrates