Animation Sets
August 16, 2025
August 16, 2025
It's been a long time since I've posted, but I've recently had some time to work on Chronicle again as the project I was working on at Zenimax online was "indefinitely shelved" and I've found myself with some free time before I start on the next exciting project. So with this time I've dug back into Chronicles animation systems, added some new features and cleaned up some things. As part of this work I want to make some posts that dig into more of the animation systems, and to start that off I want to go over what I call animation sets which are a core part of the chronicle animation system.
At it's core an animation set is a data asset that describes all of the animation assets for a character/type of character. These animation assets include clips, blend graphs, state machines, and blend spaces(when I get around to implementing them). Clips have been covered before here and my next post will cover blend graphs and state machines.
This isn't just a flat list of assets though, animation sets are broken up into groups and tags. Groups are a collection of tags that can also be children of other groups, while tags are a simple name and asset pair.
Tags are essentially aliases to the actual animation assets. By adding this layer of indirection to all animation assets it makes sharing data(animation and other) much easier as nothing has a direct reference to specific animation data except for the animation sets. So say you wanted to play a reload animation, normally you would need to have the reload animation data live on the weapons data, and if that weapon can be used by multiple characters the weapon now has to have a list of reload animations it can play on the character carrying on it. I find this sort of data setup problematic as you know have character specific data being defined not on the character itself and it can make keeping track of and finding where this data comes from difficult. By using tags the weapon can now just contain a simple "RifleReload" tag that should be played for a reload, and the actual asset that tag resolves to is all defined on the character that actually plays that animation(the weapon itself would also have an animation set if it's animated). This also means that if you add a new character type to your game you don't have to then go and update every weapon to provide it's specific reload animation since that is all defined in the set that lives on the character you just added.
Adding this layer of indirection isn't with out it's issues though as when you're looking at/working with tags in data it's not always super clear what actual animation will play for that tag, so it's important to build in tooling to make finding that information as easy as possible.
Groups are essentially a list of tags, but with the caveat that groups have parents. Every animation set has a "Default" group, this group is the only group with no parent and be thought of as the root group of an animation set. The default group is also essentially always loaded if there is a character using the animation set. The default group is meant to contain the required animation assets for the core functionality of the character, so all your basic locomotion and core gameplay animation assets end up in this group.
Non-default groups aren't loaded until something requests them to be. To stick with the above weapon example you can think of your default group as your unarmed animations, and then add a group for each weapon type. So when the character equips a weapon it would request that the "Rifle" or "Pistol" group are loaded. Multiple groups can be loaded at once so there's nothing preventing a character from having both the Rifle and Pistol groups loaded at the same time, but I will cover how tag look-up works a bit later.
The reason that groups have parents is so that every group doesn't need to define re-define every tag needed by the character just the tags that the group wants to override or add. When resolving a tag to the actual asset first the active group is checked for the tag, if it's not found in that group it will then check that parents group, all the way up to the default group. This makes it so that say the Rifle group only wants to override the walk animation, and add a reload then the rifle group would just have those 2 tags in it keeping additional groups as small and manageable as possible.
Above you can see a quick demo of the animation editor and specifically editing an animation set. The UI could use some updates to more clearly show how groups are parented, but I don't have sets that are complicated enough that having a flat list is really an issue. From the video you'll notice the animation editor is built around animation sets, as the preview window requires you to select an animation set and group and then you can preview, open and edit assets via the tags list on the right side. As mentioned above it's important to build the tooling around the idea of sets as well since without that it can be hard to keep track of what assets tags actually resolve to, and by making the sets a core part of the workflow it becomes more natural to work with.
Like the tooling, the runtime animation system is built around sets. This means that every animated character/object requires an animation set to function. As chronicle is an object and component based engine there is of course an animation component that is used to add animation to an object, this component is where the things like the animation set live.
Currently there's not a ton of data that lives on the animation component other than the all important animation set. The Start Tag tells the animation component what tag should be played once the objects animation data is all loaded. I'm not covering animation graphs/state machines in this post, but the start tag is usually the root level graph the character wants to use instead of a clip but a clip is also valid. The boolean for Random start clip is mostly used for performance testing when I spawn a bunch of the same animated object but want all of them to be playing different animations.
While the animation component itself holds the reference to the animation set it doesn't directly do much work, all animation work is done by the AnimInstance class. The component waits for the animation set asset to be ready then creates an AnimInstance with it.
Above you can see a snippet of the anim instance class and hopefully get an idea of how it's responible for all the actual animation work that happens, and the animation component is just call these functions on it's anim instance. The anim instance is the interface for all communication to and from animation with the rest of the game/engine.
I want to call special attention to SetCurrentGroup, LoadGroup, UnloadGroup, and GetTag. In the editor swapping groups is just a drop down menu, but at runtime with the ability to have multiple groups from the same set loaded at once it gets a bit more complicated. SetCurrentGroup sets the active group for the anim instance, this will be the group that is used to resolve an alias including traversing it's parent groups if need be. GetTag as you can see takes an optional groupOverride parameter, this is how having multiple groups loaded at once works, if no group override is provided it will use the active group, but if a groupOverride is provided it will use that group instead to resolve an alias. These group overrides will mostly come from the blend graphs and state machines which I'll cover more in the future. Since the animation instance only knows to load the default group on it's own Load/UnloadGroup functions are provided and will mostly be called by the animation component when it gets group load/unload requests via the messaging system. Since groups are game state/context specific it means that sources outside of animation are the ones making those requests instead of trying to bake gameplay state/context into the animation systems in some way. When a group is loaded all it's parent groups will also be loaded.
At runtime the AnimationSet resource is just a mapping of tags to resource paths, while the AnimSetInstance is a mapping of tags to ResourceHandles of loaded/loading assets from those paths. The anim instance creates an AnimSetInstance from the AnimSet provided. Because the AnimSet is a resource which means it's non-mutable at runtime this AnimSetInstance is what manages loading/unloading the referenced assets in an animation set and is used for actively resolving tag requests to this loaded resources. Having this set instance does mean we double up on some maps of tag/asset pairs but it since most active anim instance won't have all groups of a set loaded at once this bit of overhead is far outweighed by the ability to load/unload animation assets based on the state of the game and it's animation needs instead of having to load all animation data all the time.
A video showing multiple groups in action using group overrides to dynamically drive what animations are being played while using the same underlying animation graphs.
The control parameters in the anim instance debug window are the variables that drive the animation graphs and state machines which will be covered in my next post, but you can see the simple load-out component dynamically driving the weapon1 and 2 groups which are used as group overrides so that unarmed, rifle, pistol and bow all use the same underlying graphs but because of the set, group and tags resolve to different animations.
<--- Previous Post Next Post --->