动画状态机(API)是一种用于控制和管理动画切换的工具,你可以用它添加各种动画状态及其之间的转换规则,使角色或动画对象能够在不同动作之间自然的切换。
代表 动画状态机 中的一个单独状态,即某一时刻动画系统中播放的动画。,比如“站立”、“跑步”或“跳跃”。每个状态都有一个对应的 动画片段 。完整的API请参考AnimatorState。
| 属性 | 功能说明 |
|---|---|
| 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 状态。
用于定义 动画状态机 中两个 动画状态 之间的转换规则和条件。它决定了何时以及如何从一个 动画状态 切换到另一个 动画状态 。完整的API请参考AnimatorStateTransition。
| 属性 | 功能说明 |
|---|---|
| Duration | 过渡时长,时间为相对目标状态的归一化时间,默认值为 0.25 |
| Offset | 目标状态向前的偏移时间,时间为相对目标状态的归一化时间,默认值为 0 |
| ExitTime | 起始状态过渡开始时间,时间为相对起始状态的归一化时间,默认值为 0.75 |
| Solo | 使选定的动画过渡成为唯一活动的状态。其它动画过渡将被忽略。 |
| Mute | 禁用选定的动画过渡 |
| Conditions | 过渡通过的条件,如果有多个条件,只有所有条件都满足时,才会开始过渡。 |
Solo 和 Mute 通常用于调试,用于帮助开发者更高效地测试和调试 动画状态机 。
将 动画状态 连接到entry上你导出的项目运行时就会自动播放其上的动画,而不需再调用 animator.play。点击 动画控制组件(详细介绍)绑定的 实体 ,即可预览动画。
你可以通过 animatorStateMachine.addEntryStateTransition 方法让 动画状态 连接到 动画状态机 的 entry
const animatorStateMachine = animator.animatorController.layers[0].stateMachine;
animatorStateMachine.addEntryStateTransition('Idle');将两个想要过渡的 动画状态 连接即可实现动画过渡的效果, 点击两个 动画状态 间的连线,可以修改动画过渡的参数调整效果。
点击连线你可以设置 动画过渡 的参数,如添加条件:
若添加多个条件,只有所有条件都满足时,才会开始过渡。
你可以通过为 动画状态 添加 动画过渡 实现 动画状态 间切换的过渡效果。
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);
}
}
);每个 动画层 都有一个 动画状态机 ,动画最终的表现由多个 动画层 进行混合而成,如何使用 动画层 可以参考动画层文档。