动画状态机(AnimatorStateMachine)是一种用于控制和管理动画切换的工具,你可以用它添加各种动画状态及其之间的转换规则,使角色或动画对象能够在不同动作之间自然的切换。
代表动画状态机中的一个单独状态,即某一时刻动画系统中播放的动画。,比如“站立”、“跑步”或“跳跃”。每个状态都有一个对应的动画片段。
属性 | 功能说明 |
---|---|
Name | 修改 AnimatorState 的名字,名字在所在的层要是唯一的。 |
AnimationClip | 用于绑定 AnimationClip 资产,AnimationClip 存储了模型的动画数据。 |
WrapMode | AnimatorState 是循环播放还是播放一次,默认为 Once 即播放一次。 |
Speed | AnimatorState 的播放速度,默认值为 1.0 ,值越大动画速度越快 |
StartTime | AnimatorState 从 AnimationClip 的哪个时间开始播放,时间为相对 AnimationClip 时长的归一化时间。默认值为 0 ,即从头开始播放。 例如:值为 1.0 ,则是 AnimationClip 的最后一帧状态。 |
EndTime | AnimatorState 播放到 AnimationClip 的哪个时间结束播放,时间为相对 AnimationClip 时长的归一化时间。默认值为 1.0 ,即播放到最后。 |
StateMachineScripts | 允许开发者编写自定义的脚本逻辑,以在动画状态机的不同事件(如状态进入、退出、更新等)中执行特定的代码。它类似于 Script,但专门用于动画状态机。 |
entry
: 用于表示动画状态机的进入点。进入动画状态机时,总是先进入 entry
,然后根据定义的转换条件跳转到其他状态。 entry
本身不会播放动画,它主要用于连接状态机的起点到初始动画状态。通常情况,你应该将它连接到角色或动画对象的默认状态,比如角色的 Idle(站立)
状态。
any
:允许动画状态机中任意状态发生特定条件时跳转到目标状态,这对于处理全局事件或紧急动画(如受伤、死亡)非常有用。
any
状态具有最高的优先级,因此使用 any
需要谨慎,因为它可以打破当前动画的正常流转,可能会导致动画过渡不自然的情况。开发者需要确保只有在明确的条件下才使用 any
进行转换。
exit
:表示动画状态机的退出点。当状态机进入 exit
时,通常意味着状态机结束。状态机将重新进入 entry
状态。
用于定义动画状态机中两个状态之间的转换规则和条件。它决定了何时以及如何从一个 AnimatorState 切换到另一个 AnimatorState。
属性 | 功能说明 |
---|---|
Duration | 过渡时长,时间为相对目标状态的归一化时间,默认值为 0.25 |
Offset | 目标状态向前的偏移时间,时间为相对目标状态的归一化时间,默认值为 0 |
ExitTime | 起始状态过渡开始时间,时间为相对起始状态的归一化时间,默认值为 0.75 |
Solo | 使选定的动画过渡成为唯一活动的状态。其它动画过渡将被忽略。 |
Mute | 禁用选定的动画过渡 |
Solo 和 Mute 通常用于调试,用于帮助开发者更高效地测试和调试动画状态机。
将动画状态(AnimatorState)连接到entry
上你导出的项目运行时就会自动播放其上的动画,而不需再调用 animator.play
。点击动画控制组件(Animator)绑定的实体,即可预览动画。
你可以通过 animatorStateMachine.addEntryStateTransition 方法让动画状态连接动画状态机的 entry
const animatorStateMachine = animator.animatorController.layers[0].stateMachine;
animatorStateMachine.addEntryStateTransition('Idle');
将两个想要过渡的 AnimatorState
连接即可实现动画过渡的效果, 点击两个动画间的连线,可以修改动画过渡的参数调整效果。
点击连线你可以设置动画过渡(AnimatorStateTransition)的参数,如添加条件:
若添加多个条件,只有所有条件都满足时,才会开始过渡。
你可以通过为 AnimatorState
添加 AnimatorTransition
实现动画状态间的过渡。
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");
通过这样的方式你之后每次在该动画状态机所在的层播放 walk
动画时都会在播放一半时开始过渡到 run
动画。
你可以通过 animatorStateTransition.addCondition 方法为动画过渡添加条件
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);
通过这样的方式你之后每次在该动画状态机所在的层播放 idle
动画时并且 speed
参数大于 0
时,动画将过渡到 walk
动画。
你可以通过 animatorController.addParameter 方法来添加参数,animatorController.removeParameter 方法来添加参数
const animatorController = animator.animatorController;
animatorController.addParameter('speed', 0);
// 删除参数
animatorController.removeParameter('speed');
你可以通过 animator.setParameterValue 方法来设置参数的值
class AnimationScriptExample extends Script {
animator: Animator;
onStart() {
this.animator = this.entity.getComponent(Animator);
}
onUpdate(deltaTime: number) {
const inputManager = this.engine.inputManager;
// 如果用户按下 W 键,则设置 speed 参数值为 1, 符合切换到走路状态的条件,动画切换到走路
if (inputManager.isKeyHeldDown(Keys.KeyW)) {
this.animator.setParameterValue('speed', 1);
}
// 如果用户松开 W 键,则设置 speed 参数值为 0, 符合切换到站立状态的条件,动画切换到站立
if (inputManager.isKeyUp(Keys.KeyW)) {
this.animator.setParameterValue('speed', 0);
}
}
}
你可以通过 animator.getParameterValue 方法来获取参数的值:
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")
}
}
}
你可以通过 animator.getParameter 方法来获取参数对象:
class AnimationScriptExample extends Script {
animator: Animator;
onStart() {
this.animator = this.entity.getComponent(Animator);
const speedParameter = this.animator.getParameter('speed');
// 设置 speed 的默认值
speedParameter.defaultValue = 0;
}
}
将动画状态连接到 exit
状态,状态机会退出并重新进入 entry
,使得整体流程循环
你可以通过 state.addExitTransition 方法让动画状态连接动画状态机的 exit
const runState = animatorStateMachine.addState('run');
runState.addExitTransition();
通过这样的方式你之后每次在该动画状态机所在的层播放 Run
动画时都会播放结束就会进入 exit
状态之后重新从 entry
开始。
你可以为每个动画状态添加状态机脚本,它可以让你在动画状态机的不同事件(如状态进入、退出、更新等)中接收到回调详见。
状态机脚本提供了三个动画状态周期:
onStateEnter
:动画状态开始播放时回调。onStateUpdate
:动画状态更新时回调。onStateExit
:动画状态结束时回调。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)
如果你的脚本不用复用的话你也可以这么写:
state.addStateMachineScript(
class extends StateMachineScript {
onStateEnter(
animator: Animator,
animatorState: AnimatorState,
layerIndex: number
): void {
console.log("onStateEnter: ", animatorState);
}
}
);
每个动画层(AnimatorControllerLayer)都有一个状态机,动画最终的表现由多个层进行混合而成,如何使用动画层(AnimatorControllerLayer)详见