DevBlog: Multi-file Animations
Edict
Now that we have the technical capability for multi-file animations we need to integrate this functionality into our engine.
Our current convention is to use files with the name 'model.fbx' as geometry sources, and '[email protected]' as animation sources. [1] And up until this point we’ve gotten away with directly specifying that uninfected agents used 'manModel.fbx' and infected agents used 'Zombie.fbx' within the simulation code (I know, I know…).
Loading
For FBX file used in Edict we create a corresponding JSON configuration file. These are not provided to players, but drive the compilation parameters for each asset before it is shipped.
This setup has a number of useful properties:
-
JSON is a very flexible way to specify these parameters.
-
It keeps the configuration directly adjacent to the asset rather than deeply within the buildsystem.
-
It allows us to request alterations to a file’s contents without actually baking these changes into the original file. It is better to keep the original files pristene.
Geometry
{ "scale": 0.1687558009806587 }
All FBX assets support the 'scale' attribute which requests resize of the entire model/skeleton.
I try to keep units within the engine in metres. But sometimes this isn’t convenient for an animator, or we need to use models from outside the project which can’t be expected to conform to all our local conventions.
Animation
{
"animation": {
"rules": [
{ "rule": "rename", "name": "Take 001", "target": "walk" },
{ "rule": "velocity", "name": "walk", "value": 1.8 }
]
},
}
FBX assets that contain animations have access to a collection of post-processing rules, specified as an ordered list of updates. This allows us to conform the contents to our asset conventions, and specify paramater values which aren’t otherwise available (eg, target velocity of an animation).
Some of the supported rules include:
- rename
-
Change the name of the animation. It allows us to rename animations that would otherwise clash, or to update names that aren’t suitably descriptive.
- delete
-
Remove the animation from further processing. Some external assets include animations that we don’t (yet) support. It’s often easier to just remove them.
- velocity
-
Set the ground speed at which an animation expects the model is moving. This is required so that we know how fast to play back the animation to avoid skating.
At the end of the model/animation compilation process we end up with a file with a name like [email protected]
. It contains graphics data formatted in a way that’s trivial to load, and with the post-processing rules baked in.
When the game starts the engine will scan the installed directory that contains the compiled resources. Each file will be installed into a cache in memory that is indexed by keys consisting of the filename (to simplify referencing the asset from other configuration files), and a runtime generated integer (for internal use where efficiency is paramount).
Linking
Now that the raw data is accessible to the engine we need a way to describe what model and animation each agent type should use. To this end we have one more configuration file; anything residing in the directory render/agent
will name the resources that are used to render each agent.
{
"model": "manModel",
"animations": {
"run": "zombieRun",
"walk": "zombieWalk",
"idle": "zombieIdle"
}
}
The files are named after the infection types that the simulation uses and are automatically associated with agents based on their current infection status. [2]
The configuration file specifies the key for the model, and the key for each known animation type (currently 'idle', 'walk', 'run'). [3] When we load this at runtime we combine the two keys so that, for example, 'zombieRun' becomes '\manModel@zombieRun'. This keeps the names we use for our asset storage and the engine cache consistent, and should reduce the chance of referencing the wrong file.
When it comes time to actually render the agent it’s mostly a question of indexing a few arrays. We look up the infection status, then the bundle of model/animation keys; lookup the current animation, and upload the interpolated bone transforms; finally dispatch a request to render the model key.
Testing
I had a few false starts when specifying this system. One option allowed arbitrary animation names but this flexibility complicated the implementation. Another option allowed much more flexibility in the way asset keys were specified, but that would have resulted in two methods of referring to assets and made it easier to specify mismatched models and animations.
The last experiment was a lot easier to implement than I was expecting, and was the most robust. While there are a few issues with the exact values we use for zombie animation speed and velocities, the underlying features seem to meet our current needs.
TODO
For at least the coming week I’ll be focusing on features and fixes that make the current iteration of Edict substantially easier to interact with. We want to get binaries in the hands of testers soon, and keep the build up-to-date for rapid feedback and gameplay iteration.
Most immediately this will involve the implementation of a fixed camera system, visual feedback on the purpose of regions that the user has placed, and smoothing out the rough edges on the current UI system.