Animation State Editor

User-centered tool for creating in-game animation state machines

Since I am passionate about and enjoy creating user-friendly tools for game development, I decided to create an editor for our game animators that vastly speeds up our animation pipeline.

The Animation State Editor allows artists to quickly test animations and their transitions in-game, without needing to wait for loading screens, compile times, or busy programmers. It also has important features that can push the animation quality to the next level, like animation blending and global animation layers.

Connecting nodes is as simple as holding down right click and dragging. The connections also snap to the nearest center of each side to avoid spaghetti.

Features & Development

Connecting animation nodes

I started out working on linking nodes together, since they decide how the graph runs. The links decide the transition time and contain conditions that, when met, trigger a transition.

Each object in the world owns a blackboard — this is what allows us to trigger animations with in-game variables. For example, our object could be a player controller which is currently in a sprinting state. This could be added to its blackboard and read from our graph to trigger a sprinting animation on the player.

When the variable “Walking” equals true, we blend from the Idle animation to the Walk animation.

How blending looks for both the edges and the skeleton.

How animations and transitions work

Every frame we evaluate our current node and iterate over all connections. If we have one or more conditions, we allow a transition only when at least one of these are met. Otherwise we wait until our current animation clip has finished playing and transition afterwards.

Every time we transition, the target node becomes our current node and we set — or blend towards — that nodes animation.

Blending works by decomposing the transform matrix of each animation into the translation, rotation, and scale. These are then lerped (spherically lerped for the rotation quaternion) based on how for along the blend time we are. Our edge also displays a little animation, giving feedback to the user that two animations are blending.

An aiming layer playing on top of an idle animation, from the joint spine_01.

Layers are sorted such that the one with lowest priority is evaluated last (highest precedence)

Global animation layers

I took inspiration from the Unity Animator Controller for this one: their system contains one base layer and any amount of global layers which are constantly evaluated, and I felt that this method was easy to understand, scalable, and could create powerful layers for ones animation.

In my graph, the user can select a parent joint, a mask ID, as well as exclude mask IDs, which decide which joints are affected by the layer.

When an animation layer is being added to the skeletons base layer, it starts at the selected parent joint before iterating over all its child joints, skipping any joints that aren’t in that layers mask as well as any joints that are in the exclude mask.

Animation State Editor also supports any number of global layers, which is where the fields priority and weight become useful. The user can decide how much a layer affects the skeleton with weight which also uses blending: the base layers transform matrix is blended towards the layer matrix, using the weight as a blend factor.

Layers are also sorted by priority when evaluated. This matters, for instance, when two non-additive layers with a weight of 1 are evaluated. The second layer evaluated would override the current matrix, no matter what the result of the first layers matrix multiplication was. As such, layers with lower priority value take precedence when multiple have high weight values.

Content browser at work. Uses a table structure with a dynamic amount of columns to allow smooth resizing. Clicking an item imports it into the graph.

Important quality of life features

From the very start, I knew I needed the Animation State Editor to feature a real time preview window, to make iterative work that much easier for game animators. The preview window is displayed next to the graph and has features like the Blackboard Testing window, allowing one to test how in-game variables could affect the animations.

Initially, I ran into issues like things not updating correctly or sometimes crashes. After working on the editor for some time, I learned how important it is to have readable and modular code, such that I could add, test, and verify data coming from editor changes without hassle, speeding up the work process significantly once my code was cleaned up.

Later into the process I added a content browser, since I found it annoying to manually drag and each animation into the editor for use. The content browser finds all animation FBX files under a Content folder and verifies that the animations were made and function for the currently selected skeleton. All matching animations are then added to the content browser.

Conclusion

Future plans

I intend to keep working on Animation State Editor until my time at The Game Assembly comes to an end, as I hope it’s a useful tool for game project 8. I plan on adding support for 2D blend spaces as well as having more extensive testing and feedback sessions with our game animators once the development of our next game project starts.

What I learned

I never expected this journey to be as helpful for me as it was. Working on and designing Animation State Editor gave me a lot of insight on usability, quality of life, and the importance of feedback from the target audience.

I also learned how to plan and set goals for myself; almost all of my agile development and planning has been in the context of game projects done in groups, but now I got to teach myself about setting a path for an individual passion project, from which I learned my own pace and limits.