SetAnimation
native void SetAnimation(Name animName, double framerate = -1, int startFrame = -1, int loopFrame= -1, int endFrame = -1, int interpolateTics = -1, int flags = 0) — play-scoped version
native ui void SetAnimationUI(Name animName, double framerate = -1, int startFrame = -1, int loopFrame= -1, int endFrame = -1, int interpolateTics = -1, int flags = 0) — UI-scoped version
Usage
If the calling actor has a 3D model attached, and said model has named animation sequences in it, this function will play the specified animation sequence on the model. Note, currently IQM is the only model format that supports named animation sequences.
This allows for a simple way to play a specific model animation without relying on the tedious process of binding separate key frames to sprite names in MODELDEF. For this to work, the model must include multiple named animation sequences. For example, in Blender they can be created through the use of Nonlinear Animation.
Note: There are several prerequisites in order to be able to use this feature: 1. The actor must have the DECOUPLEDANIMATIONS flag. 2. The model must include multiple named animation sequences. 3. The MODELDEF definition for the model must have the BaseFrame keyword. |
Scopes
SetAnimation can be called on an actor pointer from play scope, while SetAnimationUI can be called from UI scope. This means that the latter cannot be called from an actor state (since those are play-scoped). The other difference is that SetAnimationUI allows for truly instant animation change, rather than on the next tic.
Parameters
- Name animName
- The name of the animation sequence to play.
- Note: in Blender this is the name of the action (can be seen in the Dope Sheet), not the name of the Nonlinear Animation strip.
- double framerate
- The framerate at which the animation sequence should play. If not specified, the sequence's own framerate will be used.
- int startFrame
- The frame of the animation sequence to start at. By default starts at the beginning.
- int loopFrame
- If specified and the SAF_LOOP flag is used, after the animation has played in full once, it'll keep looping from this frame instead of startFrame.
- int endFrame
- If specified, the model's animation will only play until this frame. Otherwise, will play until the end of the animation.
- Note, this argument wasn't initially present when this function was originally added to the engine, but it is present in GZDoom 4.12.
- int interpolateTics
- The number of tics to interpolate between the model's current animation sequence and the new sequence set with animName. A negative value is interpeted as 1 tic.
- int flags
- Multiple flags can be combined with
|
. The following flags are available:- SAF_INSTANT - By default, when setting a new animation, the model will interpolate from its current animation to the new one for the duration set by interpolateTics. This flag disables it.
- SAF_LOOP - If set, the animation will be looped until a new animation is set. If not set, the sequence will be played from start to end and stop at the final frame.
- SAF_NOOVERRIDE - If set, the animation will not start if the same animation is already playing. Mostly intended to be used alongside SAF_LOOP when the function is being repeatedly called with the same animName, so it doesn't get continuously restarted.
Examples
An actor with a single looped animation
This actor will start the model's 'idlebreathing' animation action as soon as it spawns. Note, since this actor is meant to be a decoration and it uses no state logic, its states don't need any duration.
Model PulsingThing
{
Path "models/props"
Model 0 "pulsingthing.iqm"
Skin 0 "pulsingthing.png"
BaseFrame // Decoupled animations REQUIRE this
}
class PulsingThing : Actor
{
Default
{
+DECOUPLEDANIMATIONS
}
States
{
Spawn:
M000 A -1 NoDelay SetAnimation('idlebreathing', flags:SAF_LOOP);
stop;
}
}
A monster
However, for any regular actor that needs state logic (such as a monster), normal state logic rules will still apply. For example, if a Zombieman was created using decoupled animations, its state code could look like this:
class Zombieman3D : Zombieman
{
States
{
// This will call A_Look as usual, once per 10 tics,
// and the idle animation is looped. SAF_NOOVERRIDE
// makes sure the idle animation isn't constantly
// restarted:
Spawn:
M000 A 0 A_Look();
M000 A 10 SetAnimation('idle', flags: SAF_LOOP|SAF_NOOVERRIDE);
loop;
// Same logic with walking animation:
See:
M000 A 0 A_Chase();
M000 A 4 SetAnimation('walk', flags: SAF_LOOP|SAF_NOOVERRIDE);
loop;
// As usual, zombieman takes 10 tics to aim,
// then fires and returns to See 16 tics later.
// It is implied that you've crafted your animations
// in a way that they will match these durations.
// Splitting aiming and firing makes it easier:
Missile:
M000 A 10
{
SetAnimation('aim');
A_FaceTarget();
}
M000 A 16
{
SetAnimation('fire');
A_PosAttack();
}
goto See;
// The pain animation is started right away,
// but the sound is delayed by 3 tics, since
// this is how the default Zombieman does it:
Pain:
M000 A 3 SetAnimation('pain');
M000 A 3 A_Pain;
goto See;
Death:
M000 A 5 SetAnimation('death');
M000 A 5 A_Scream;
// The remaining states can be combined
// into one, since nothing else needs
// to happen here logic-wise:
M000 A -1 A_NoBlocking;
stop;
XDeath:
M000 A 5 SetAnimation('gibbed');
M000 A 5 A_XScream;
M000 A -1 A_NoBlocking;
stop;
Raise:
M000 A 20 SetAnimation('resurrect');
Goto See;
}
}
Important note: All functions that change states (such as A_Look and A_Chase in the examples above) must be placed before a SetAnimation call, specifically, on a different state. The reason is, state changes can be performed instantly, and when a monster wakes up, it can easily call A_Look, then A_Chase, and then enter its Missile/Melee state on the same tic. If the SetAnimation call also happens on the same tic, the actor will try to start several animations on the same tic, which can cause issues such as the next animation not playing and the actor being stuck in its previous animation or not moving.