Animation

AnimatorStateMachine

Introduction

The AnimatorStateMachine (API) is a tool used to control and manage animation transitions. You can use it to add various animation states and their transition rules, allowing characters or animated objects to switch naturally between different actions.

Main Components

Animation State

Represents a single state in the AnimatorStateMachine, i.e., the animation being played at a certain moment in the animation system, such as "Standing", "Running", or "Jumping". Each state has a corresponding Animation Clip. For the complete API, please refer to AnimatorState.

PropertyDescription
NameModify the name of the AnimatorState, which must be unique within the layer.
AnimationClipUsed to bind the AnimationClip asset, which stores the animation data of the model.
WrapModeWhether the AnimatorState plays in a loop or once, the default is Once, which means it plays once.
SpeedThe playback speed of the AnimatorState, the default value is 1.0, the larger the value, the faster the animation speed.
StartTimeThe time from which the AnimatorState starts playing the AnimationClip, the time is the normalized time relative to the duration of the AnimationClip. The default value is 0, which means it starts playing from the beginning. For example, if the value is 1.0, it is the last frame of the AnimationClip.
EndTimeThe time at which the AnimatorState stops playing the AnimationClip, the time is the normalized time relative to the duration of the AnimationClip. The default value is 1.0, which means it plays to the end.
StateMachineScriptsAllows developers to write custom script logic to execute specific code during different events (such as state enter, exit, update, etc.) of the AnimatorStateMachine. It is similar to Script but specifically for the AnimatorStateMachine.
Three Special AnimatorState in the Editor

entry: Represents the entry point of the AnimatorStateMachine. When entering the AnimatorStateMachine, it always enters entry first, and then jumps to other states according to the defined transition conditions. entry itself does not play animations; it mainly connects the starting point of the state machine to the initial AnimatorState . Typically, you should connect it to the default state of the character or animated object, such as the character's Idle state.

any: Allows any state in the AnimatorStateMachine to transition to the target state when specific conditions are met. This is useful for handling global events or emergency animations (such as being injured or dying).

The any state has the highest priority, so use any with caution, as it can disrupt the normal flow of the current animation, potentially leading to unnatural animation transitions. Developers need to ensure that any is used only under clear conditions.

exit: Represents the exit point of the AnimatorStateMachine. When the state machine enters exit, it usually means the state machine ends. The state machine will re-enter the entry state.

Animation Transition

Used to define the transition rules and conditions between two AnimatorState in the AnimatorStateMachine. It determines when and how to switch from one AnimatorState to another. For the complete API, please refer to AnimatorStateTransition.

PropertyDescription
DurationThe transition duration, the time is the normalized time relative to the target state, the default value is 0.25
OffsetThe forward offset time of the target state, the time is the normalized time relative to the target state, the default value is 0
ExitTimeThe start time of the transition from the initial state, the time is the normalized time relative to the initial state, the default value is 0.75
SoloMakes the selected animation transition the only active state. Other animation transitions will be ignored.
MuteDisables the selected animation transition
ConditionsThe conditions for the transition to pass. If there are multiple conditions, the transition will only start when all conditions are met.

Solo and Mute are usually used for debugging, helping developers test and debug the AnimatorStateMachine more efficiently.

Using the AnimatorStateMachine

Default Playback

Editor Usage

Connecting the AnimatorState to entry will automatically play the animation on it when you run the exported project, without needing to call animator.play. Click the entity bound to the Animation Control Component (detailed introduction) to preview the animation.

Script Usage

You can use the animatorStateMachine.addEntryStateTransition method to connect the AnimatorState to the entry of the AnimatorStateMachine.

const animatorStateMachine = animator.animatorController.layers[0].stateMachine;
animatorStateMachine.addEntryStateTransition('Idle');

Animation Transition

Editor Usage

Connecting two AnimatorState will achieve the effect of animation transition. Click the line between the two AnimatorState to modify the parameters of the animation transition to adjust the effect.

Click the line to set the parameters of the AnimatorStateTransition, such as adding conditions:

If multiple conditions are added, the transition will only start when all conditions are met.

Script Usage

You can add an AnimatorStateTransition to the AnimatorState to achieve the transition effect between AnimatorState.

const walkState = animatorStateMachine.addState('walk');
walkState.clip = walkClip;
const runState = animatorStateMachine.addState('run');
runState.clip = runClip;
const transition = new AnimatorStateTransition();
transition.duration = 1;
transition.offset = 0;
transition.exitTime = 0.5;
transition.destinationState = runState;
walkState.addTransition(transition);
animator.play("walk");

In this way, every time you play the walk animation in the animation layer (detailed introduction) where the AnimatorStateMachine is located, it will start transitioning to the run animation halfway through.

Adding Conditions to Animation Transitions

You can use the animatorStateTransition.addCondition method to add transition conditions to the AnimatorStateTransition.

const idleState = animatorStateMachine.addState('idle');
idleState.clip = idleClip;
const walkState = animatorStateMachine.findStateByName('walk');
const transition = new AnimatorStateTransition();
 
transition.addCondition(AnimatorConditionMode.Greater, 'speed', 0);
 
transition.destinationState = walkState;
idleState.addTransition(transition);

In this way, every time you play the idle animation in the layer where the AnimatorStateMachine is located and the speed parameter is greater than 0, the animation will transition to the walk animation.

Adding/Removing Animation Parameters

AnimatorControllerParameter are used to control the AnimatorState transitions of the AnimatorStateMachine. By setting the value of the AnimatorControllerParameter to meet the conditions of the AnimatorStateTransition, the AnimatorStateTransition will be triggered, switching to the target AnimatorState.

You can use the animatorController.addParameter method to add AnimatorControllerParameter and the animatorController.removeParameter method to remove AnimatorControllerParameter.

const animatorController = animator.animatorController;
animatorController.addParameter('speed', 0);
// Remove parameter
animatorController.removeParameter('speed');
Setting Animation Parameter Values

You can use the animator.setParameterValue method to set the value of AnimatorControllerParameter.

class AnimationScriptExample extends Script {
  animator: Animator;
  onStart() {
    this.animator = this.entity.getComponent(Animator);
  }
 
  onUpdate(deltaTime: number) {
    const inputManager = this.engine.inputManager;
 
    // If the user presses the W key, set the speed parameter value to 1, meeting the condition to switch to the walking state, and the animation switches to walking.
    if (inputManager.isKeyHeldDown(Keys.KeyW)) {
      this.animator.setParameterValue('speed', 1);
    }
    // If the user releases the W key, set the speed parameter value to 0, meeting the condition to switch to the standing state, and the animation switches to standing.
    if (inputManager.isKeyUp(Keys.KeyW)) {
      this.animator.setParameterValue('speed', 0);
    }
  }
}
Getting Animation Parameter Values

You can use the animator.getParameterValue method to get the value of AnimatorControllerParameter:

class AnimationScriptExample extends Script {
  animator: Animator;
 
  onStart() {
    this.animator = this.entity.getComponent(Animator);
  }
 
  onUpdate(deltaTime: number) {
    const speed = this.animator.getParameterValue('speed');
    if (speed === 1) {
      console.log("The player is walking")
    }
  }
}
Getting Animation Parameter Objects

You can use the animator.getParameter method to get the AnimatorControllerParameter object:

class AnimationScriptExample extends Script {
  animator: Animator;
 
  onStart() {
    this.animator = this.entity.getComponent(Animator);
    const speedParameter = this.animator.getParameter('speed');
    // Set the default value of speed
    speedParameter.defaultValue = 0;
  }
}

AnimatorStateMachine Animation Loop

Connecting the AnimatorState to the exit state will cause the AnimatorStateMachine to exit and re-enter the entry, making the overall process loop.

Script Usage

You can use the state.addExitTransition method to connect the AnimatorState to the exit of the AnimatorStateMachine.

const runState = animatorStateMachine.addState('run');
runState.addExitTransition();

In this way, every time you play the Run animation in the Animation Layer where the AnimatorStateMachine is located, it will enter the exit state after playing and then start again from entry.

State Machine Scripts

You can add State Machine Scripts to each AnimatorState, allowing you to receive callbacks during different events (such as state enter, exit, update, etc.) of the AnimatorStateMachine see details.

Script Usage

StateMachineScript provide three AnimatorState cycles:

  • onStateEnter: Callback when the AnimatorState starts playing.
  • onStateUpdate: Callback when the AnimatorState updates.
  • onStateExit: Callback when the AnimatorState ends.
class theScript extends StateMachineScript {
  /**
   * onStateEnter is called when a transition starts and the state machine starts to evaluate this state.
   * @param animator - The animator
   * @param animatorState - The state be evaluated
   * @param layerIndex - The index of the layer where the state is located
   */
  onStateEnter(animator: Animator, animatorState: AnimatorState, layerIndex: number): void {
    console.log(`Enter ${animatorState.name}`)
  }
 
  /**
   * onStateUpdate is called on each Update frame between onStateEnter and onStateExit callbacks.
   * @param animator - The animator
   * @param animatorState - The state be evaluated
   * @param layerIndex - The index of the layer where the state is located
   */
  onStateUpdate(animator: Animator, animatorState: AnimatorState, layerIndex: number): void {
    console.log(`Update ${animatorState.name}`)
  }
 
  /**
   * onStateExit is called when a transition ends and the state machine finishes evaluating this state.
   * @param animator - The animator
   * @param animatorState - The state be evaluated
   * @param layerIndex - The index of the layer where the state is located
   */
  onStateExit(animator: Animator, animatorState: AnimatorState, layerIndex: number): void {
    console.log(`Exit ${animatorState.name}`)
  }
}
 
animatorState.addStateMachineScript(theScript)

If your script does not need to be reused, you can also write it like this:

state.addStateMachineScript(
  class extends StateMachineScript {
    onStateEnter(
      animator: Animator,
      animatorState: AnimatorState,
      layerIndex: number
    ): void {
      console.log("onStateEnter: ", animatorState);
    }
  }
);

Multi-layer AnimatorStateMachine Blending

Each Animation Layer has an AnimatorStateMachine, and the final animation performance is blended by multiple Animation Layer. For how to use Animation Layer, please refer to the Animation Layer documentation.

Was this page helpful?