I’ve been spending my spare time doing some game development in ClojureScript, and recently I needed to solve the problem of dealing with animations for the player character. Having used the Unity game engine in the past, I knew that finite state machines (FSM) were a great way to manage transitioning between different animations for a character, based on underlying state changes.
For my game, I am currently dealing with 4 animations:
idle- When standing still
run- When moving
jump- When jumping
attack- When attacking
These animations come from the Kenney Character Assets bundle
So, I spent some time building out a very poor FSM implementation for managing the animation states and transitioning the animations when the character’s state changes due to player input.
I soon learned that my FSM implementation was not going to cut it as my animation needs were a bit more advanced than I originally thought. I found two issues:
jumpanimations were mutually exclusive, but the
attackanimation actually needs to be blended with the other animations.
- For the
attackanimation, I also wanted to emit an event at a particular time in the animation to signal that the “hit” procedure should execute, as the sword-swing doesn’t really land until about 1/2 second into the animation.
I was going to start researching the different FSM libraries available for Clojure, when, literally the next day, I saw a post on the Clojure subreddit about a great library called clj-statecharts by Lucy Wang.
After reading through the documentation, I decided to try using it to replace my terrible FSM implementation.
Here’s a simplified version of what my character animation FSM looks like in clj-statecharts:
A few things to note about this FSM:
- The only event ever sent is the
:tickevent, which is fired on every frame. I make extensive use of guarded transitions to determine if a transition should be made based on the underlying game-state. This is represented by all of the predicates (
- The undisclosed
play-animation!function deals with playing the animation via the
- The undisclosed
emit-event!function simply emits an event to character’s event-system
- I’m using parallel states of
:lower-bodyto separate the animation layers. So the character can be in both a
:runstate for the
:attack-startstate for the
- I’m using delayed transitions to handle emitting the event after 450ms have passed since the attack animation started.
- I’m using a nested state machine for the
:groundedstate to toggle between the
Overall, I’m really happy with how well clj-statecharts worked for this. It feels much easier to manage than my previous hacky FSM system, and it will be a very useful tool to have for other aspects of my game.
Here’s what the result looks like in-game: