Introduction for Programmers

Overview

CryMannequin is set of high level tools that together help you manage the complexities of game animation.

The Middleman

CryMannequin sits in between your game code and CryAnimation. Your game code keeps track of game state. CryAnimation is the low level animation system that plays and blends animation clips and allows for modification of joint hierarchies.

CryMannequin glues these two systems together: It shields the game code from working with these low level concepts and the translation between the two heavily uses data that animators can set up.

Data-Driven at its Core

Like many modern animation systems CryMannequin was designed to be data-driven at its core. If used well we believe this leads to fewer bugs, more reuse, more consistency and more malleable systems.

Combined with a WYSIWYG approach it allows for the people making the animation to be more directly involved in how they appear in the game. And finally, it helps with experimentation which is crucial when you want to innovate.

Big data driven games also means "big data". You have to manage lots of animation and lots of people will have to work together. CryMannequin helps here by providing tools to organize your animation data (tags, spreading out over different files, etc).

Describes Animation Logic, Not Game Logic

All this talk about data-driven animation is great, but you don't want to drive this too far. CryMannequin is not a black box "motion graph" (or CryEngine AnimationGraph) that decides for you exactly how to go from one state to another. It's hard to control graph structures like this and we recognized the need to control animation state in code. So CryMannequin focuses on managing the animation and some of the transition logic but explicitly not your state logic. Thus you will also not see your typical hierarchical finite state machine (HFSM) in CryMannequin. Your code is still in the driving seat; even though CryMannequin provides data-driven transition support you still have control over how those transitions are selected and played back. On top of that you can still easily synchronize your code with animations whenever you want to.

Quick Overview of the Main Concepts

The Basic Building Block - Fragments

Instead of starting a specific animation directly you will first have to wrap it into what we call a Fragment. Fragments are created through the editor and represent the first in a couple of layers which hide the animation sequencing details from the game code. A fragment is simply a layered animation sequence. Below you see an example of this: 3 animations combined into one fragment.

We have a base layer containing a sequence of 2 animation clips ("stand_tac_melee_rifle_3p_01" and "stand_tac_melee_rifle_3p_02") and a second layer which contains an additive animation clip ("stand_tac_melee_scar_add_3p_01"). If you want you can make the sequence a lot longer or add many more layers. You can also transition from one clip to another, speed up clips, or loop or cut them up. All of this is very similar to other nonlinear animation tools you might be familiar with.

Fragments aren't limited to animation clips though: you can also insert procedural clips which can be anything from clips that drive IK (aiming, looking, etc) to attaching objects, to playing sound effects. Below we extend the example with a procedural clip playing a sound effect and another procedural clip that executes entity alignment code.

How the Code Refers to Animation - FragmentIDs

The game code doesn't request fragments directly though. Every fragment gets categorized under a specific name we call a FragmentID. The FragmentID represents an animation state like "Moving", "Idling", "Reloading", "Firing", etc. The game requests fragments by specifying this category, this FragmentID. Contrary to what the name 'FragmentID' suggests there are typically many fragments that fall under the same FragmentID (it is named like this for historical reasons). For example you can have many different "Moving" fragments: they represent either random variations or context-specific variations (moving while crouched, moving while standing, etc).

By starting fragments indirectly through a FragmentID we decouple the game code from the actual fragments (and Transitions - see below). Typically somebody defines a FragmentID for every basic animation state and then animators create fragments for those FragmentIDs. This decoupling is actually where the name CryMannequin comes from: it provides a structure in which animators can 'dress up'.

Adding Variation - Tags and Options

We can assign the same FragmentID to multiple fragments that all will now become variations for that FragmentID. To guide the selection process each fragment is labeled with Tags.

The tags, for example "crouched" or "machineGun" or "scared", provide a simple way to specify a context under which you want specific fragments to be selected.

For example the game can include the "crouched" and "machineGun" tag in the global animation context to guide the selection process. A fragment will only be selected when all of its tags match with tags specified in the global animation context.

SAnimationContext &context = pActionController->GetContext();
context.state.Clear();
context.state.Set(TAG_ID_CROUCHED, true);
context.state.Set(TAG_ID_MACHINEGUN, true);

In this example a fragment with tags "crouched+machineGun" can be selected, as well as "machineGun", "crouched" or a fragment with an empty set of tags "". But fragments with other tags like "swimming" or "plasmaRifle" cannot be selected.

Still there can be more than one fragment with the same set of tags for a certain FragmentID. Think of these as (possibly random) variations. Each one automatically gets an index which is called an 'option index'. By default a random option index will be chosen, but the game code can select a specific one if it needs some more control over the selection process. For example, this comes in handy when streaming in animations: Say you have 20 different variations but you like to stream in only one of them because anything else would be wasteful. Then you can override the random selection process and make sure that the specific variation you streamed in is selected.

Sequencing Fragments Together - Transitions

Fragments like the above are sequenced together to form your game's animation.

Say you've got a fragment with FragmentID "Moving":

The top of the image shows what the game requests, the bottom shows which animations CryMannequin translates this into.

And say you have another fragment with FragmentID "Idling":

What happens if the game requests them in sequence? The animations will be sequenced together automatically with a standard blend in between them:

Now we can define a more complicated Transition in between them. The system gives you the possibility to specify exactly how the individual layers within these fragments get sequenced during the transition and gives you the ability to add new clips in between the clips. For example below a transition is created where an 'idle2move' animation is placed in between the idle and move animation.

Note that the game still requests exactly the same thing: "Idling" followed by "Moving". But notice that there is now an orange area (called a Transition or sometimes Transition Fragment) that pushes back the beginning of the "Moving" fragment. This transition can be set up completely in data by animators such that it gets inserted whenever we go from "Idling" to "Moving".

Scopes

Typically different parts of the same character are in different animation states. For example you might be playing a basic idle animation on the main skeleton, and use an independent override animation to add some random looking around. Or you might want to play another independent idle animation on the weapon the character is carrying. Sometimes you'd also like to synchronize those different parts, and sometimes you don't. CryMannequin has tools to help you set this up. The different 'parts' we mentioned before are called Scopes. Another way of thinking about them is as 'high level animation channels'.

By definition each of these scopes can have at most one fragment playing at any time (or it can be transitioning from one fragment to another as shown before).

As the weapon example above already shows: a CryMannequin setup is not limited to one character instance (ICharacterInstance) or one entity (IEntity). Each scope in the setup can point to a different character instance or even to a different entity. The editor will allow us to edit all the fragments that play on the different scopes as one synchronized whole if we need to.

Which scopes you have and what they are used for is fully configurable. For example here are some of the scopes used in the SDK example:

Scope

Description

FullBody1P

Plays fragments that affect the full 1st person character (maps to layers 0-2 in the CryAnimation transitionqueue of that character).

FullBody3P

Plays fragments that affect the full 3rd person character (layers 0-2).

Aimpose

Plays assets for parametric aiming (layer 4).

Torso3P

Plays fragments on the torso of the 3rd person character (layers 9-11).

Weapon

Plays fragments on the weapon.

WeaponForceFeedback

Plays fragments that contain procedural clips that execute force feedback.

WeaponSound

Plays fragments that contain procedural clips that play weapon sound.

AttachmentTop

Plays fragments for the top attachment of the weapon.

SlaveChar

Plays fragments on the 'enslaved' character when doing a stealth kill or other synchronized animation.

Interfacing with Animation - Actions

To request a FragmentID you have to create an instance of what we call an Action. Here is how you might request a "Reload" FragmentID in code:

IActionPtr pAction = new TPlayerAction(..., FRAGMENT_ID_RELOAD, ...);
pActionController->Queue(pAction);

The Action (TPlayerAction) in the previous example is the class that will remain alive while the fragment is playing and can be used to communicate with the system.

It is possible to poll the Action's status, for example to check whether the fragment was installed: (it could be delayed because of a transition for example)

if (pAction->GetStatus() == Installed) ....

or (better) the Action can be overridden and can contain animation specific logic that executes alongside the fragment or communicates with the rest of the game.

class TReloadAction : public TPlayerAction
{
  ...
  virtual void Enter() {
    TBase::Enter();

    SetIKParameters();
    pPlayer->SendEvent(PLAYEREVENT_RELOAD_ANIMATION_STARTED);
  }
  
  virtual void Update(..) {
    TBase::Update(..);

    UpdateIKParameters();
  }
  ...
}

By default an action just requests the one FragmentID you pass in by the constructor, but it is possible to change this request while the fragment is running and by doing so to create mini animation-state-machines when it makes sense. In many cases you don't need to though: the default CryMannequin transition system for example already handles the common "intro - middle - outro" scenarios.

How does this tie in to the scopes we mentioned above? Each FragmentID typically has a set of scopes assigned to it. For example the "Reload" from above could be bound to both the "FullBody" and "Weapon" scope. When the game requests a FragmentID the system looks up which scopes are assigned to this FragmentID. Next the system will (roughly) go through each of these scopes and look for a matching fragment. Each scope typically has its own set of fragments to choose from so each scope will typically be playing a different fragment. To the game code it still looks as if it is executing one Action, but in effect it will span multiple scopes and CryMannequin will manage the synchronization. In the given example the "FullBody" scope plays the reload animation on the main skeleton, while the "Weapon" scope plays an animation on the weapon that is properly synchronized with that "FullBody" fragment.

This makes it possible to run multiple actions in parallel, each on their own set of scopes. For example a special action could be playing an independent idle fragment on the "Weapon" scope while the "FullBody" scope can be playing something else entirely. Each scope can only be controlled by a single action though and a priority system handles the possible conflicts.

When Not to Use CryMannequin

As with every system it is important you know the limitations of the system before you dive in and use it in production. Here are some of the things you might want to use CryMannequin for besides animation:

Can you describe Game State with it?

Though CryMannequin can be used to describe game states, be wary: CryMannequin was developed for sequencing animation. Even though animation and game state have to run largely in parallel it still makes sense to keep track of game state in another structure. For example in its current form the CryMannequin sequencing itself is not extendable. It is configurable, but only up to a certain degree. You will run into problems if you try to set up game logic inside CryMannequin.

What if you only have a small amount of or very simple animation?

If you only need to sequence together a very small amount of animations CryMannequin might be overkill. CryMannequin does scale down relatively well though, and it can be used in many different ways with varying levels of complexity.

Can it sequence Music?

In theory it seems doable - but there is a better solution for this: see the MusicLogic editor in the Database View.

Can it sequence Sound? Effects?

If you want to synchronize your sounds & effects with animation, CryMannequin is a good fit. You might need to create a couple of extra scopes for these, so they can run independently when needed. Extra scopes can also save you from some copy-paste work when the same sound needs to be played for many different base animations. We used CryMannequin for all weapon sound effects in Crysis 3 as they were typically tied to an animation. But the sound & effect support is still limited so be wary (for example you cannot see the length of a sound like you can see the length of an animation).