This post is going to detail the character animation system I implemented in “Idle Raiders” to support dynamic equipment (changeable weapons, helmets, etc.). The system was designed to be data-driven, i.e. new equipment types and animations can be added to the game without modifying any game code.
Similar functionality is common in many games, but finding detailed explanations about the inner workings of these system was surprisingly difficult. I want to make it clear that none of this is particularly complicated. Actually, it ended up being very simple in terms of algorithmic complexity. But when working in these things, even the trivial often eludes your sight because you usually need a broad encompassing understanding of the problem and the nooks and crannies involved to come up with a way to solve a concrete problem (no matter how simple the solution might turn out to be). For this reason I decided to write it up, so that others will have an easier time in the future.
About the game
Idle Raiders is an 2D “RPG character management” idle game, where you try to manage a group of ‘raiders’ while they try to best ever stronger growing monsters and “raid” ( a term used in MMOs) dungeons for precious loot and other goodies. You, as the player, only have indirect command in controlling the raiders: You can change their equipment and abilities, build a home base town for them, roughly determine the current activity they pursue, and do other things to make sure the raiders are prepared for what they want to do, but otherwise, the raiders are on their own. During combat, they act alone, and you can’t directly control them.
The game is gearing up for its beta release, and an (admittedly rough and buggy) alpha is playable on on Kongregate, should you wish to try it out.
Old and new workflow, from the content creator’s perspective
In the current publically available version of the game, all characters in the game use ordinary spritesheet animation. Here’s a sheet section for the Warrior’s animations (might be blurred on your browser, the game’s WebGL renderer displays it in a more pixelated look):
We licensed a character spritesheet from Oryx Design Lab and heavily modified it to suit our needs (since by default it only contains idle animations), and also added some entirely new drawings, such as the dragon from the second raid. The workflow just consisted of our artist drawing something in his favorite image manipulation program, and then creating animation data (frame timings, etc.) for use inside the game (at first with a small custom tool, later with Spriter). Nothing complicated, but also very time consuming. Since actually drawing each individual frame took such a long time, most animations only have very few frames in general: With few exceptions, all consist of two or three of them. Of course, changing equipment was practically impossible given this setup. At most we could support multiple equipment sets per creature type, where everything (armor and weapons) would be changed at the same time. Creativally quite limiting. For us… and the players. So we looked for something better.
Pixels with skeletal animation
In the new workflow, to support dynamic equipment parts, we create animations as skeletal animations in an animation tool (we use Spriter, but Spine for example would probably work as well), where body parts used in the various animations are split up into distinct pieces which are animated separately (more on that later).
These “equipment parts” can be changed individually at runtime. Here’s what that looks like in action by the way (all work in progress of course):
We quickly discovered that this was a much quicker way to add animations in general, although initially we feared that animating body parts by interpolating them around would mess with the pixelated art style of the previous spritesheet animations. This would have been very problematic – smooth animations just wouldn’t fit the lo-fi 16 bit look the entire game has.
Fortunately we quickly found out that what determines the look of spritesheet animations is not the way the body parts actually move, but instead the very limited number of frames in each animations and the resulting choppy look. By making generous use of ‘instant’ position, rotation, and scale changes, the new animations are visually not really distinguishable from ones that are drawn manually using frames in a spritesheet, precisely because they look “choppy”. Here’s a comparison of an animation where everything is smoothly interpolated, vs the same animation where everything is just snapped into place:
Note that just limiting the frames per second during playback of the animation is often not enough, because that forces all frames to have the same duration, where changes in the animation can only occur on the boundary time between two frames, and often more varied timings than that are required.
We wanted new animations and equipment parts to “add themselves” to the game, without us having to change any code, or without having extra steps in the production pipeline where we for example have to edit some files and add a new reference to a new equipment part.
In order to facilitate this, we ended up using a combination of simple naming schemes for the animations and the files which represent different body parts. Two things in particular seemed good about that idea: It was simple to implement, as we didn’t have to write any new editors or things like that which take care of all the metadata, and everything that’s required for the artist to make new content data driven are things that would already have to be done in a non-data driven pipeline (where for example one of us coders adds new equipment parts into the code manually): It’s only done by naming files and animations (albeit with a specific syntax that is easy to understand).
So how does all of this work?
In animation tools like Spriter and Spines the animations and their skeleton/bone definitions are grouped together as “entities” (or similar concepts), which are basically supposed to contain all animations for a particular character type. For example, we have the “basic_humanoid” entity which contains all animations that are used for humanoids of varying sizes (like the raiders themselves):
As you can see, it contains various animation types for various weapon types. The naming here is important:
The entity name can be anything, and in the game is used to hook up entities in the game with the defined animations.
The animation names themselves define the different weapon types for that entity type in the game. When you create a new animation and name it “attack_arbitraryname” you define a new weapon type “arbitraryname” which will have its own animations. The name “arbitraryname” will later be used to look up equipment types for that weapon. You can optionally create custom animations for weapon types (such as idle_arbitraryname or walk_arbitraryname) if you want to have weapon type-specific animations, and the system falls back to defaults if no specific animation can be found.
There are some more extras to this naming scheme: For example, you can see “attack_1h2” in the list above. Our weapon animations support multiple variations which are used to make animations look less repetitive, and by adding a suffix number “x” where x is the number of the variation. In the example above, 1 handed weapons (“1h”) have two variations and the game automatically alternates between the two.
Not seen above are some extras like being able to define animations for specific weapons (by adding “@weaponname” as a suffix, for example “@inferiorkatana” or “@superioreuropeanbroadsword”) or specific entity sub types (by adding “!warrior” or “!archer”). You can imagine anything you want here, I think the naming scheme for animations themselves works well for anything that might be specific/different on an animation-by-animation basis.
The next part is the definition of the body parts an entity and its animations consist off. Here’s an example of the body parts for the “attack_spear” animation:
All of these are connected to a bone hierarchy, but that is not important and more about making animation for the artist as comfortable as possible, so I omit it here. Once again the names are important. They, too, will later be used to look up textures for new equipment types.
Different animations can use arbitrary different amounts and types of body parts, but body parts which represent the same thing (such as the head of the body) all share the same name in different animations. This is important because when we switch out equipment parts in the game, that named body part in the animation is essentially replaced. If we had something like “head001” in one animation and “head002” in another animation, we would have no direct way of recognizing that both of these are semantically the same thing, unless we apply some more parsing shenanigans.
It’s important to remember that body parts here are not ‘expected’ in the code: Our artist could add tentacles to basic_humanoids and the game would automatically load them and support the addition of new equipment parts for the tentacle body part. This is important because this allows us to create all kinds of monster types with different body makeups without adapting the code that runs it at all.
The last question we have to answer is how to add new equipment parts. This is easy, the equipment parts are just placed into special directories for each entity type, and then named using yet another simple naming scheme. The game’s engine takes care of the rest. Here are two examples for the “1h” weapon type and the “head” body part:
Their file system locations including the directories will look something like “animation_directory/basic_humanoid/head/head_xxxx.png” where basic_humanoid is taken from the entity name and “head” is taken from the body part name. The game will use this information to locate the correct texture for a body part when you, say, equip the “redarmor” helmet of the “head” body part. In this screenshot (and with all other body types we have currently), the names of the files also follow a syntax (“bodypartname_arbitraryname.png”) but that is optional: It’s nice because the names will be unique even if we should later decide to place all files into the same directory for whatever reason.
All animations are authored using some default armor set (in our case just the textures for the Warrior, and various default texures for different weapons), but animation tools should be able to swap them out temporarily to see if a body part fits (in Spriter, there are “character maps”).
Note that doing it this way does in fact allow for grossly disproportionate body part textures (such as huge heads or a sword where the handle for some reason is in a different position), the only thing you need to make it work is some way to define “origins” or “centers” of the textures where they are overlaid on the actual bone in the animation. In Spriter, we do this using the “pivot point” feature that allows us to set these origins on a per-file basis.
Making it run
Since we are now entering the code side of things, I want to mention that the ‘data driven’ part of this system only concerns the creation of new content and making it available in the game for easy use. If you want to, for example, have an ingame item of a certain type change the visual equipment part in a character’s animation, you will still have to take that internal definition of your item and hook it up with the correct animation sprite, a part of the system that says ‘when equipping this item, now set the texture of the sprite for the sword to a new texture named ‘sword_bloodysword.png’.
That is another part of content creation (creating items, in this example) and can also be solved in data driven manner. Explaining that is not part of this article, however. I may write articles about that in the future, because we have many simple and easy to use systems to create new items, abilities, quests, ‘timed stat changes’ (buffs) etc. in the game to make content creation easier, and those might be worth a read as well.
In explaining how we went about implementing this data driven system, I’m not going to go into how we actually compute and display skeletal animations . If you want to replicate our system, Spine and Spriter (probably other software, too) have implementations available for most popular languages so this should not be a problem. What I’m going to explain in the following is what kind of data we took out of our animation project, and what we did with it.
Running the system in practice is not much more than keeping track of certain data and updating it when you change equipment. To set it up we extract some basic information from the entire animation project and the file system:
- entity and animation names
- body part names
- all available body part images
- pivot points for all the body part images
We use this data to create several lookup tables for stuff that will be commonly accessed, like
- entity_type => [array of weapon types]
- weapon type => ([list of weapon-specific animations],[list of creature-type specific animations])
- weapon type => [list of animation variations for that weapon type]
Then we setup some data every time we create new creatures in the game (like the raiders, or monsters and NPCs). For each of these creatures we store
- Rendering related data (like all the sprites for the different body parts)
- The active weapon type
- Default values for all the equipment parts. The default equipment parts are actually merged together as a single “set”, i.e. we store the “defaultEquipmentSet”: “archer” and when returning to default sprites the system uses head_archer.png, leftfoot_archer.png, rightfoot_archer.png, etc.
- Default weapon type, and the default for the concrete weapon (e.g. default type “sword” and default weapon “firesword”)
In addition to the above, we store loads of additional data like animation durations, animation speeds, etc. But those are specific to our game. The data mentioned above should be more than enough to get the system running.
Changing equipment at runtime
The runtime part of the system exposes a function to change equipment parts of each body part or weapon. As parameters, the function takes the character to be modified, the body part name to be modified, and the equipment part type to be used and then does its magic. Like
This function is then used by our item system when equipping an item that is supposed to change the look of an equipment part.
In the implementation of this function we compute the actual filename for the respective image from the body part type name and the name of the concrete equipment part, and replace the texture on the sprite that is used to draw that body part with that image. In the game, when the player unequips all items on a character, the system goes back to the default values, which is why we store them when creating new creatures.
When changing equipment, it may be necessary to update certain data in whatever you use to actually run animation computations. For example, if the pivots/origins/’centers’ change with updated images from new body parts, you need to tell that part of your system about it, because that might change transformation matrices or other things internally and result in buggy animations otherwise.
The last important part of the runtime implementation is how we actually run animations. When animating a type of animation (for example, attack animations when a raider is fighting) the programmer doesn’t specify the variation of an animation. Instead, we call high level functions in the animation system like “animateAttack(character)” and “animateIdle(character)” which automatically take care of properly executing animations that represent what the programmer is trying to do. These functions internally do a number of things:
- If an animation has multiple variations, the system somehow (for example just randomly uniform) selects one of them.
- Computes the actual animation name from the type of animation that is to be played (idle, walk, attack, etc.), the character’s entity type, active weapon type, and active weapon
- Change the actual animation in the internal skeletal animation system.
The last bit is the actual updating of animation computations. Every rendering frame we call an update function in the animation system which takes care of the following things:
- Animations may not need to run at 60 FPS. Skip some frames in that case.
- Run the update of actual animation computations in the internal skeletal animation system.
- Update the visibility of body part sprites depending on the active animation (these may also change during the animation itself). E.g. shields are not visible when a character is using a two-handed weapon.
- Update Z-ordering
- Take care of properly repeating and ending animations (idle and walk animations for example loop, while attacks typically do not)
And that is it! You now run a flexible animation system with dynamic equipment!
Using animation data for gameplay: Query at runtime, not at creation time!
Animations have information contained within them that are used for gameplay, such as their durations (to make sure the character doesn’t move around while executing an animation) or certain events in the animation (such as the point in time when an arrow is sent flying from the bow).
Before updating the animation system from our old spritesheet-only ways, we used to read that data once when animations where loaded, and then hand that to abilities (such as a basic melee attacks or ranged attacks where characters fire a projectile) on creation.
However, supporting multiple animation variations means that this information can change when a different variation is executed. You might have a quick sword stab that takes half a second, and a variation where the sword is swung like a cleaver which takes 1.5 seconds.
The solution obviously is to query this data every time an animation is executed, not just once at creation time. We solved this by maintaining some internal state in the animation system which tracks the current animation each character is executing, and then just query the durations and event timings from that.
An alternative solution to this (which we don’t use, because so far we don’t need it) is to give the responsibility of picking animation variations to gameplay code. Abilities could use this to chain together specific types of animations for combos, for example. This was not an important feature for us, so we decided to hide it behind the slightly higher level animation system API to make animation usage simpler.
What our system doesn’t do (yet), and future work
For the future there are various different areas of interest I would like to explore for our animation system.
We could use animation blending to automatically automatically create animations for activities such as idling and walking by blending together a default animation with default weapon stances for each weapon type.
I would like to add dual wielding (e.g. carrying dagger in left hand, sword in right hand) to the engine at some point. This is non-trivial because if you want to allow any combinations of one handed weaponry, you need some way to automatically transfer a weapon animation from one hand to the other, for example, unless you want to create left and right handed versions of all weapon animations. Since another bit of content creation overhead would be unacceptable, most things here should be data driven and procedurally generated.
We render separate sprites for each body part which can be batched on most platforms. However, this is sometimes not possible (such as HTML5 Canvas). Because we are going for ‘spritesheet’ look anways in our use case a reasonable optimization might be to render the animations into atlas textures with a fixed framerate and later render sections of that atlas in its entirety instead of rendering single body parts. I will have to investigate the results in performance, which might turn out to be nothing if canvas engines end up doing proper batching in the background anyways. I will also have to investigate effects on the visuals since fixed framerate might lead to degradations in how it looks in edge cases, and also aliasing issues when rendering to and from textures might result in problems. Not to mention that using this system for all characters in the game might lead to high texture memory requirements.
If you have dealt with this before I’d love to hear how you solved this problem in your games and if you did anything differently. In particular, if you know about any cool stuff we could do with this new (or related) technology, or noticed that we horribly botched something at some point, be sure to let me know in the comments!