Menu
Lumberyard
Developer Guide (Version 1.12)

Time in the Animation System

The Animation system uses different units of 'time,' depending on the system. How those units of time compare is best explained using an example.

The definition of 'frames': The Animation system uses a fixed rate of 30 frames per second (fps). Of course, games can run at higher frame rates, but some operations in the Editor that use the concept of 'frames'—or operations that clamp the animation duration to 'one frame'—assume a frame rate of 30 fps.

Assume then that you have an animation with a duration of 1.5 seconds. This means that the animation has 46 frames (note that this includes the final frame). So, in the case of Real Time, assume an animation starts at time 0, has no segmentation, and is played back at normal speed. However, rather than using Real Time, the Animation system typically uses Animation Normalized Time. This is compared with Real Time in the following table:

Frame Index Real Time (seconds)* Animation Normalized Time**
0 0.0 s 0.0
1 0.033.. s = 1/30 s 0.022.. = 1/45
.. .. ..
30 1.0 s 0.666.. = 30/45
.. .. ..
44 1.466.. s = 44/30 s 0.977.. = 44/45
45 1.5 s = 45/30 s 1.0

* Real time is used to define duration:

  • Duration = lastFrame.realTime - firstFrame.realTime. That's 1.5s in our example.

  • IAnimationSet::GetDuration_sec() returns the duration of an animation.

    Note: For a parametric animation, this returns only a crude approximation—the average duration of all its examples, ignoring parameters or speed scaling.

  • CAnimation::GetExpectedTotalDurationSeconds() returns the duration of an animation that is currently playing back.

    Note: For a parametric animation, this returns only a crude approximation, assuming the parameters are the ones that are currently set and never change throughout the animation.

  • No function exists that returns the Real Time of an animation. To calculate that, you must manually multiply Animation Normalized Time with the duration.

** Animation Normalized Time:

  • Time relative to the total length of the animation.

  • Starts at 0 at the beginning of the animation and ends at 1 (= RealTime/Duration = Keytime/LastKeyTime).

  • Used by functions such as ISkeletonAnim::GetAnimationNormalizedTime() and ISkeletonAnim::SetAnimationNormalizedTime().

  • Is not well-defined for parametric animations with examples that have differing numbers of segments. For more information, see the following section, Segmentation.

Segmentation

In practice, the animation system does not use Animation Normalized Time; this terminology was used to make the introduction easier to understand. Typically, Segment Normalized Time is used. To understand Segment Normalized Time, you must first understand segmentation.

For time warping (phase matching) purposes, animations can be split into multiple segments. For example, to time warp from a walk animation with 2 cycles to a walk animation with 1 cycle, you have to annotate the first animation and split it into two (these are segments). To achieve this segmentation, you must add a segment1 animation event at the border between the cycles.

Note

An animation without segmentation has exactly 1 segment, which runs from beginning to end.

Segmentation introduces a new unit for time, Segment Normalized Time, which is time relative to the current segment duration.

Extending our example further, observe what happens when a segment1 animation event at 1.0s is added to split the animation into two segments.

Frame Index Real Time AnimEvents (Animation) Normalized Time Segment Index* Segment Normalized Time**
0 0.0 s 0.0 0 0.0
1 0.033.. s 0.022.. 0 0.033.. = 1/30
.. .. .. .. ..
30 1.0 s segment1 0.666.. 1 0.0
.. .. .. .. ..
44 1.466.. s 0.977.. 1 0.933.. = 14/15
45 1.5 s 1.0 1 1.0

* Segment index:

  • Identifies which segment you are currently in. Runs from 0 to the total number of segments minus 1.

  • While an animation is playing, you can use CAnimation::GetCurrentSegmentIndex() to retrieve it.

  • When using ca_debugtext or es_debuganim, then this index is displayed after "seg:".

** Segment normalized time:

  • Time relative to the current segment's duration.

  • 0 at the beginning of the segment, 1 at the end (only 1 for the last segment, as you can see in the table).

  • While an animation is playing, you can use CAnimation::Get/SetCurrentSegmentNormalizedTime() to get or set the Segment Normalized Time.

  • As the names suggest, CAnimation::GetCurrentSegmentIndex() retrieves the current segment index and CAnimation::GetCurrentSegmentExpectedDurationSecondsx() retrieves the duration of the current segment.

  • When representing time within parametric animations, it is more convenient to use Segment Normalized Time than Animation Normalized Time; therefore, Segment Normalized Time is used at runtime.

  • AnimEvent time is specified using Animation Normalized Time (except for the special case of parametric animation; see the following section).

  • When using ca_debugtext or es_debuganim, Segment Normalized Time is displayed after "ATime:". Following that, the real time within the segment and the segment duration are displayed within the parentheses.

Playback Speed

Playback speed does not impact the functions that compute duration of playing animations, such as CAnimation::GetExpectedTotalDurationSeconds() or ISkeletonAnim::CalculateCompleteBlendSpaceDuration().

Segmented Parametric Animation

Animation Normalized Time, Segment Index, and Duration all create ambiguity for segmented parametric animations. This is because each example animation within the parametric animation can have its own number of segments. To avoid ambiguity, animation events in or on segmented parametric animations use Segment Normalized Time. As a result, an animation event will be fired multiple times (once per segment) during the animation.

  • ISkeletonAnim::GetAnimationNormalizedTime() uses a heuristic: It currently looks for the example animation with the largest number of segments and returns the animation normalized time within that example.

  • ISkeletonAnim::GetCurrentSegmentIndex() uses a different heuristic: It currently returns the segment index in the example animation, which happens to be the first in the list.

Given this, we are considering redefining the above based on the following observation: You can define the total number of segments in a parametric animation as the number of segments until repetition starts.

So, say you have a parametric animation consisting of 2 examples—one with 2 segments and the other with 3 segments. This will start to repeat after 6 segments (the lowest common multiple of 2 and 3). However, you can uniquely identify each possible combination of segments using any number from 0 to 5.

The Character Tool uses this method to achieve a well-defined duration. The ISkeletonAnim::CalculateCompleteBlendSpaceDuration() function calculates the duration until the parametric animation starts to repeat (assuming the parameters remain fixed). It reverts to the regular GetExpectedTotalDurationSeconds() implementation for non-parametric animations so that the function can be used in more general situations.

Animation with Only One Key

Normally your animations have at least two keys. However, when you convert these into additive animations, the first frame is interpreted as the base from which to calculate the additive, leaving only 1 frame in the additive animation (this means that, in respect to the asset, both the start and end time of the asset are set to 1/30 s).

Functions retrieving the total duration of this animation will return 0.0 (for example, IAnimationSet::GetDuration_sec(), ISkeletonAnim::CalculateCompleteBlendSpaceDuration(), and CAnimation::GetExpectedTotalDurationSeconds()).

However, for playback purposes, the animation system handles these animations as if they have a duration of 1/30th of a second. For example, Animation Normalized Time still progresses from 0 to 1, while real time goes from 0 to 1/30th of a second. CAnimation::GetCurrentSegmentExpectedDurationSecondsx() also returns 1/30th of a second in this case.

Direction of Time

Time typically cannot run backward when playing an animation. You can move time backward only if you do it manually by setting the flag CA_MANUAL_UPDATE on the animation and using CAnimation::SetCurrentSegmentNormalizedTime. See the example DGLINK CProceduralClipManualUpdateList::UpdateLayerTimes().

Time within Controllers

Different units are used for controllers that contain the actual key data and are used for animation sampling.

Frame Index Real Time I_CAF Ticks* Keytime**
0 0.0 s 0 0.0
1 0.033.. s 160 1.0
.. .. .. ..
30 1.0 s 4800 30.0
.. .. .. ..
44 1.466.. s 7040 44.0
45 1.5 s 7200 45.0

* I_CAF Ticks:

  • Used within I_CAF files to represent time

  • There are 4800 I_CAF ticks per second (this is currently expressed by the fact that TICKS_CONVERT = 160 in Controller.h, which assumes 30 keys/second)

** Keytime

  • Used at runtime to pass time to the controllers for sampling animation

  • Used within CAF files to represent time

  • A floating point version of 'frame index'

  • Can represent time in between frames

  • Use GlobalAnimationHeaderCAF::NTime2KTime() to convert from Animation Normalized Time to Keytime

  • All animation controllers in the runtime use Keytime

Animation assets can also have a StartTime other than 0.0s—this complicates matters slightly, but only for the controllers. Typically, for everywhere but the controllers, time is taken relative to this StartTime.