Slow Rush Studios logo,
    depicting an apprehensive-looking snail rushing forward

Slow Rush Studios

◂  Baby Break
News index
Timing Animations  ▸

Back in Motion

Contents

I'm back, and now only slightly sleep deprived! Yes, my Steam Deck is awesome (and Baby Mk II is doing great).

To celebrate, I decided to add some animations to player and enemy characters. They actually run and jump now!

Quality Signals

On the one hand, animations don't solve the most pressing problem this game has: the lack of a highly-compelling gameplay loop.

But on the other hand, characters "running" with all the smoothness of a floating statue makes it difficult to take this game seriously:

Would you wishlist a game where characters don't have any animation? Me either.

So working on gameplay is more important ultimately, but I do need to meet some minimum bar to get folks to play at all. 1

Animation for Monkey Brains

In 3D land, animation requires figuring out keyframes, interpolation types, root motion, animation blending, Blender's improved-but-still-tricky controls, constantly fighting with the viewport camera to get it repositioned just right, and god knows what else.

In 2D-pixel-art land, you draw some pictures and cycle between them, like a flipbook:

A spritesheet of a bouncing wand on the left, with the animated wand shown on the right
The animation frames on the left work together to create the corresponding animation on the right.
Our monkey brains love to interpret a rapidly changing picture as movement!

So, to animate a character, you just:

  1. Draw some images.
  2. Define how rapidly those images need to be swapped out. 2 3
  3. Write some code to define which sequence of images should be displayed when a character runs, jumps or falls.

Animating Rollback Networking

Except, we also have rollback networking4, which adds two complications:

I solved that by processing each frame of animation to be displayed for a certain number of ticks, and in the game state each animated character only keeps track of its current animation name5 and how many ticks6 the character has been in that animation state.

Then, for each character, the drawing logic of the game can use the stored animation name and tick count to choose the right frame to draw - which makes the drawing logic blissfully oblivious to any rollbacks. 7

Anyway, that was dangerously close to talking about code (ewww), so here's what it looks like:

Run, jump, fall and ladder climbing animations in action. Have I mentioned I'm not much of an artist?

Somehow my ladder animations make player characters look cute and chubby - but at least you can tell that they're climbing up or sliding down.

Attack animations are still a work in progress: those need to have wind-up and reload animations associated with them, which the game doesn't support yet. 8 9

Playable web build‎

Try running around as an unintentionally-cute-and-marshmallow-esque animated player character:

Press F1 for help, including to see keyboard/mouse controls. Mobile devices probably won't work! By playing you agree to our Privacy Policy.

FYI: I've disabled online web multiplayer here just so I don't have to worry about maintaining backwards- or forwards-compatibility in the matchmaking server.


1

Having animations in the game will also help me to launch a "Coming Soon" page on Steam for my game that's only "fairly embarrassing" rather than "entirely mortifying".

2

If you're using Aseprite (which I recommend) then you can define this in-app as the number of milliseconds to display each frame, from which you can derive a frames per second number.

3

Surprisingly there's no real consensus on the "right animation speed" for pixel art. I ended up animating most animations at around 12 frames per second, which feels pretty smooth.. but maybe it's a bit too high to stick with (higher frames per second means more animating work!). Strategically altering the duration of certain frames is apparently also a thing, which I played with in the animation above.

4

For those just joining us, rollback networking is where you predict what all the other (remote, far away over the internet) players are doing; if you mis-predict, you rewind ("roll back") the entire state of the world to when you made your mistake, factor in the corrected action from the remote player, then play the game state in fast forward to get back to the present time.

5

Actually, storing the full animation names is wasteful because they're heap-allocated strings, so each time the game world is copied (60 times a second) we'd be re-allocating all those strings too (or increasing a reference counter if using reference counted strings).

Instead, I take advantage of the fact that animation names aren't going to be removed ever* once they are loaded: I pre-process the animations to intern each string name as part of loading asset data, so that the game state can store a number representing each string instead.

(* well, animation names can be removed at runtime via hot-reloading assets, which would cause a crash. But that's not a problem for my players.)

6

A tick is a single forward-step of the game world.

7

Although it does mean that frames of animations are skipped if the game logic needs to run multiple times for a render (as tends to happen if rendering is the bottleneck), which is not so great.

Maybe someday I can find a best-of-both-worlds approach.

8

Currently the logic for selecting the correct animation is chosen "instantaneously" each tick, based on the movement state of a character.. which doesn't work when a character is standing still specifically because it's preparing to attack.

(This is also why none of the enemy animations have any Anticipation, like crouching down slightly before a jump.)

9

Also it's quite painful to synchronize a frame-by-frame attack animation with actually creating an (e.g.) in-game arrow at the right time and the right location, so I'm working on a better approach to that too.

◂  Baby Break
News index
Timing Animations  ▸