Advanced animation

So far we have shown how you can use the CAnimation component to play a single animation clip at once. When it comes to skeletal animation the system is capable of a few more advanced features that allow you to play and blend multiple animation clips at once, as well as other advanced functionality.

Cross fade

Often your animation system might need to transition from playing one animation clip to another (e.g. moving from a walk to a run animation). In that case you can use CAnimation::crossFade() instead of CAnimation::play(). Cross fade will slowly fade in the new animation clip while fading out the previously playing one, over the specified time period.

CAnimation animation = ...; // Set up animation component
// Play some clip normally
// Then after some time you decide to switch to a new clip, and cross fade it over 1 second
animation->crossFade(animationClip2, 1.0f);


Sometimes it is useful to sample just a single frame from an animation clip (e.g. "freezing" an enemy in place). For such purpose you can use CAnimation::sample(). Just provide the animation clip and the time to sample at.

// Sample animation clip at specified point
animation->sample(animationClip, 2.0f);


Blending comes in multiple various forms, and all of them allow you to somehow play multiple animation clips at once.

1D blending

In this form of blending multiple animation clips are placed linearly one after another. The user can then use a weight value in order to interpolate between the animations. The two animations closest to the weight value are choosen and blended together.

For example if you had different versions of walk/run animations that alternate depending on how fast the character is moving, you could set them up as a 1D blend, and then use the characters speed as the weight, ensuring the system plays the right animation depending on the speed.

To start a 1D blend operation call CAnimation::blend1D(). This method expects a Blend1DInfo structure, containing a list of animations to blend between, and their positions. It also expects the weight value that determines which animations are blended together.

// Set up a 1D blend that blends between three animations on a [0, 1] range
BlendClipInfo blendClips[3];
blendClips[0].clip = animationClip0;
blendClips[0].position = 0.0f;
blendClips[1].clip = animationClip1;
blendClips[1].position = 0.5f;
blendClips[2].clip = animationClip2;
blendClips[2].position = 1.0f;
Blend1DInfo blendInfo;
blendInfo.clips = blendClips;
blendInfo.numClips = 3;
// Blend with weight of 0.75f. This will play both animationClip1 and animationClip2, each contributing 50% of the animation
animation->blend1D(blendInfo, 0.75f);

2D blending

2D blending works similarly as 1D blending, except it uses a two-dimensional value for the weight. It also interpolates between four animation clips at once instead of two. It is limited to four clips, and a [0, 1] range. Therefore the clips can be imagined as being on corners of a square.

For example you can use this blend type to interpolate between character aiming animations in a third party game. You would adjust the 2D weight based on the vertical and horizontal directions the character is aiming and the system would interpolate between look up/down/left/right animations to animate the character in the direction the user is aiming.

To start a 2D blend operation call CAnimation::blend2D(). This method expects a Blend2DInfo structure, containing four animation clips to blend between, as well as a 2D weight determining how the animations are blended together.

// In this case you can imagine the blend square as a diamond instead (rotated 45 degrees), so its corners map with the animation directions, for a more intuitive understanding
Blend2DInfo blendInfo;
blendInfo.topLeftClip = lookLeftClip;
blendInfo.topRightClip = lookUpClip;
blendInfo.botLeftClip = lookDownClip;
blendInfo.botRightClip = lookRightClip;
// Horizontal look direction is fully towards the left (x = 0.0f), while the vertical direction is in the middle (y = 0.5f), neither up nor down.
animation->blend2D(blendInfo, Vector2(0.0f, 0.5f));

Additive blending

Additive blending serves a different purpose than from what we have seen so far. It is used to play a special type of animations called additive animations. Such animation clips need to be imported with the special additive property, as discussed earlier.

Such animation clips can then be played on top of another primary animation. For example imagine that your character has three primary animations: standing, walking and running. Now imagine you wanted to add a weapon reload animation in the game. Instead of creating three new separate animations, you can instead create an additive animation, which can then be played on top of any of the existing three primary animations.

To play an additive animation call CAnimation::blendAdditive(). This method expects the following parameters:

  • A clip to play
  • Optional weight in [0, 1] range of how much should the animation affect the result
  • Optional fade length which serves a similar purpose as the CAnimation::crossFade() method, allowing you to slowly increase the effect of the additive animation over time
  • Layer to play the animation on. Each additive animation can be played on its own layer - playing another additive animation on the same layer replaces the previous one.
// Play some clip normally
// Play a reload animation additively, slowly blending it over one second, on 0th layer
animation->crossFade(reloadClip, 1.0f, 1.0f);

To stop an additive animation you can call CAnimation::stop(). It accepts a layer index as a parameter and determines which clip to stop. Calling CAnimation::stopAll() will stop all clips, additive or otherwise.

Animation states

CAnimation component allows you to retrieve and set animation states. Animation states tell you the current state of an animation clip queued for playing in some way, including whether it is currently playing, current play time, wrap mode and more.

Call CAnimation::getState() with an animation clip as the parameter to retrieve information about that clip. The method outputs an AnimationClipState that can be used to query various information about the clip.

AnimationClipState state;
// Returns true if the clip was queued for playback (in any way) on the component
if(animation->getState(animationClip, state))
gDebug().logDebug("Animation is currently at time: " + toString(state.time));

More important that querying information, states can be used for starting playback, as well as having full control over blending and similar operations. Essentially all animation playback methods discussed so far can be implemented just by using states.

To set a clip state call CAnimation::setState(). If a particular clip is already playing its state will be updated, and if the clip isn't currently playing, playback will start according to the assigned state.

The state has the following relevant properties:

AnimationClipState state;
state.time = 2.0f;
// Start animation playback from 2 second mark
animation->setState(animationClip, state);