- 文档
- 教程
- 游戏开发
【免费下载链接】godot-docs
Godot Engine official documentation
AnimationNodeStateMachinePlayback是 Godot 引擎中用于程序化控制 AnimationTree 状态机(AnimationNodeStateMachine)播放的核心运行时对象:你不需要在编辑器里手动连线触发转场,而是通过一行$AnimationTree.get("parameters/playback")拿到它,再用travel()、start()、stop()等方法让角色在 Idle / Run / Jump 等动画状态之间按最短路径平滑切换。读完本文,你将掌握该对象的全部方法与信号,能独立写出基于状态机的角色动画控制系统,并理解travel()背后 A* 路径规划与交叉淡入(crossfade)的工作机制。
本文内容以 classes/class_animationnodestatemachineplayback.rst 类参考文档为主体,并补充了 Using AnimationTree 教程、AnimationNodeStateMachine 与 AnimationNodeStateMachineTransition 类文档中的关联细节。
一、这个类是什么:状态机的"遥控器"
在 Godot 的动画体系中,AnimationNodeStateMachine(状态机)是放在AnimationTree根节点下的一种动画根节点:它把多个AnimationRootNode组织成一张图,节点即"状态",连线即"转场",状态之间可以按最短路径自动或手动切换。
而AnimationNodeStateMachinePlayback就是附着在这张状态机图上的运行时播放控制句柄。它不是编辑器里的可视化节点,而是一个继承自Resource→RefCounted→Object的运行时对象,专门负责:
- 让某个状态开始/停止播放(
start()/stop()); - 从当前状态沿最短路径"旅行"到目标状态(
travel()); - 立刻跳到 travel 或自动前进(auto advance)给出的下一个状态(
next()); - 查询当前状态、当前播放位置、当前动画长度、是否正在播放等运行时信息;
- 报告正在进行的交叉淡入(crossfade)的源节点、长度与位置;
- 通过
state_started/state_finished信号通知状态切换的时刻。
从继承链(Resource < RefCounted < Object)可以看出,它本身是一个资源对象,且其resource_local_to_scene属性被覆盖为true——这意味着它在场景中是局部资源,场景的每个实例各自拥有独立的播放状态,不会互相干扰。
二、获取 Playback 对象
状态机的 Playback 对象不会凭空出现,它是AnimationTree节点对外暴露的一个导出参数。文档给出的标准获取方式是:
var state_machine = $AnimationTree.get("parameters/playback") state_machine.travel("some_state")C# 对应写法:
var stateMachine = GetNode<AnimationTree>("AnimationTree").Get("parameters/playback").As<AnimationNodeStateMachinePlayback>(); stateMachine.Travel("some_state");使用下标语法同样可以读取(Using AnimationTree 教程 中 StateMachine travel 一节的写法):
var state_machine = animation_tree["parameters/playback"] state_machine.travel("SomeState")AnimationNodeStateMachinePlayback stateMachine = (AnimationNodeStateMachinePlayback)animationTree.Get("parameters/playback"); stateMachine.Travel("SomeState");关键前提(来自教程原文):状态机必须先运行起来才能 travel——要么调用start(),要么把某个状态节点连接到Start端口,详见 Using AnimationTree 教程 的 StateMachine travel 小节。
三、播放控制核心 API
这组方法是整个 Playback 对象的使用主体,对应文档 Methods 表中的 4 个 void 方法。
start(node, reset = true)
state_machine.start("Idle") # 从头开始播放 Idle state_machine.start("Run", false) # 从当前时间继续播放 Run(不重置)- 作用:开始播放指定动画状态;
- 参数:
reset为true时动画从头播放,为false时从当前位置继续; - 注意
node的类型是StringName,在 GDScript 中直接传字符串字面量即可。
stop()
state_machine.stop()停止当前正在播放的动画。没有任何参数。
travel(to_node, reset_on_teleport = true)
state_machine.travel("Run") # 沿最短路径切到 Run state_machine.travel("Run", false) # 若发生传送,不重置目标动画- 作用:从当前状态沿最短路径转场到另一个状态(路径规划算法见下一节);
- 如果当前状态到目标状态之间没有连通路径,状态机会"传送"(teleport)到目标状态,此时动画直接切换;
reset_on_teleport为true时,发生传送时目标动画从头播放;为false时保留传送时的播放进度。
这里与 AnimationNodeStateMachineTransition 的priority(优先级)属性联动:优先级更低(数值更小)的转场在 travel 遍历状态图时会被优先选择。
next()
state_machine.next()如果存在由 travel 或自动前进(advance)计算出的下一条路径,立即从当前状态切换到下一个状态。适合用在"想跳过转场等待、立刻切状态"的场景。
四、travel() 的底层机制:A* 最短路径与传送
理解travel()之前,需要先了解它在状态机图上做了什么。根据 AnimationNodeStateMachine 类文档与 Using AnimationTree 教程 的 StateMachine travel 一节:
"You can instruct the graph to go from the current state to another one, while visiting all the intermediate ones. This is done via the A* algorithm. If there is no path of transitions starting at the current state and finishing at the destination state, the graph teleports to the destination state."
也就是说:
- 有路径:Godot 用A* 算法在状态图中计算从当前状态到目标状态的最短路径,并依次"途经"所有中间状态,每个中间转场按照各自配置的 xfade 时间、switch mode 执行;
- 无路径:图直接传送到目标状态(teleport),此时
travel()的reset_on_teleport参数决定目标动画是否从头播放。
路径规划只会使用由AnimationNodeStateMachineTransition连接的节点(见 AnimationNodeStateMachineTransition 描述),所以要想 travel 能走通,必须先给状态之间建立转场连线。
与此相关,AnimationNodeStateMachine上有一个allow_transition_to_self属性(默认false):若为true,允许 travel 传送回自身状态,且当travel()的 reset 选项开启时动画会被重启;若为false,传送到自身状态时什么都不发生。这在做"按一下重新播放当前动画"这类需求时很实用。
路径可视化:get_travel_path()
var path: Array[StringName] = state_machine.get_travel_path() for state_name in path: print("途经状态: ", state_name)get_travel_path()返回当前由 A* 算法内部计算出的旅行路径,类型为Array[StringName]。可以用来调试状态切换顺序、或结合 UI 显示角色下一步的动作。
五、状态查询 API(运行时信息)
这一组都是const方法(无副作用,不修改任何成员变量),用于随时读取状态机的运行状态。
| 方法 | 返回类型 | 含义 |
|---|---|---|
is_playing() | bool | 是否正在播放动画 |
get_current_node() | StringName | 当前正在播放的动画状态名 |
get_current_play_position() | float | 当前状态内的播放位置(秒) |
get_current_length() | float | 当前状态的长度(秒) |
get_current_node() 的 crossfade 语义
文档特别强调了一个容易踩坑的细节:
"When using a cross-fade, the current state changes to the next state immediately after the cross-fade begins."
一旦交叉淡入开始,get_current_node()会立刻返回下一个状态,而不是等淡入结束。所以在做"读取当前状态"的逻辑时,如果正处于过渡期,返回值已经是目标状态。同理,AnimationNodeStateMachineTransition 的xfade_time说明中也注明:状态机在淡入开始后立即切换当前状态。
get_current_length() 的复合语义
get_current_length()返回当前状态的长度,但文档给了三条重要说明:
- 任意
AnimationRootNode都可以作为状态(而不仅是单个动画),因此一个状态内可能包含多个动画,此时返回的长度取决于状态内部节点的连接方式(即哪个动画的时长"说了算"); - 如果某个转场设置了不重置(
reset = false),返回的是当时剩余的长度; - 因此它并不总是"从头到尾的完整时长",与
get_current_play_position()配合使用时要注意这一点。
六、交叉淡入(Crossfade)查询 API
当travel()或自动前进触发了带xfade_time的转场时,状态机内部会有一段"旧状态渐出、新状态渐入"的重叠播放区间。这组方法专门用来查询这段淡入过程:
| 方法 | 返回类型 | 含义 |
|---|---|---|
get_fading_from_node() | StringName | 正在淡出的起始状态名 |
get_fading_from_length() | float | 淡出源状态的长度;无淡入时返回0 |
get_fading_from_play_position() | float | 淡出源状态当前的播放位置;无淡入时返回0 |
get_fading_length() | float | 当前淡入动画的总时长;无淡入时返回0 |
get_fading_position() | float | 当前淡入动画的播放位置;无淡入时返回0 |
典型用途:
if state_machine.get_fading_length() > 0.0: var fade_from: StringName = state_machine.get_fading_from_node() var fade_progress: float = state_machine.get_fading_position() / state_machine.get_fading_length() # fade_progress 从 0 增长到 1,可用于驱动镜头、特效等这套"from + length + position"组合可以帮助你在淡入尚未结束时就感知到过渡的源状态与进度,比如实现"旧动作的收尾动作盖过新动作"之类的混合效果。
七、信号:捕捉状态切换的时刻
Playback 对象对外暴露两个信号,是驱动游戏逻辑(如触发脚步声、粒子特效、攻击判定)的常用入口:
state_started(state)
state_machine.state_started.connect(func(state): print("状态开始播放: ", state) )当state开始播放时发出。若state是一个设置为**分组模式(grouped)**的状态机,其内部信号会以"名称前缀"的方式透传出来。
state_finished(state)
state_machine.state_finished.connect(func(state): print("状态播放结束: ", state) )当state播放结束时发出。文档特别说明了它与 crossfade 的关系:
"If there is a crossfade, this will be fired when the influence of the get_fading_from_node() animation is no longer present."
即:存在交叉淡入时,state_finished会等到淡出源动画的影响力完全消失后才触发——也就是说,旧状态的"结束"信号以淡出完成为准,而不是以动画自然播完为准。这与get_current_node()"淡入开始即切换"的语义正好互补:切换是即时的,结束是等淡出的。
八、与状态机类型和转场配置的联动
Playback 对象本身不做转场决策,它执行的是 AnimationNodeStateMachine 与 AnimationNodeStateMachineTransition 配置好的规则。理解下面几点,才能把travel()/next()用得恰到好处:
状态机类型(StateMachineType)影响 start/stop 语义
AnimationNodeStateMachine的state_machine_type属性(默认ROOT)定义了转场处理的模式:
- STATE_MACHINE_TYPE_ROOT(0):回到开头视为从起始状态开始播放;转到结束状态视为退出状态机;
- STATE_MACHINE_TYPE_NESTED(1):回到开头视为回到当前状态内动画的开头;转到结束状态(或某状态没有转出连线)视为退出状态机;
- STATE_MACHINE_TYPE_GROUPED(2):分组模式,由父级状态机控制,不能独立运行,父/祖先中必须存在 ROOT 或 NESTED 类型的状态机。
当state是 grouped 状态机时,Playback 的state_started/state_finished信号会带上前缀透传,这一点在信号一节已提到。
转场属性决定 travel 的表现
在 AnimationNodeStateMachineTransition 中,影响 Playback 行为的关键属性包括:
- xfade_time / xfade_curve:淡入时间与淡入曲线,决定
get_fading_*系列方法观测到的淡入过程; - switch_mode:
IMMEDIATE(立即切换)、SYNC(立即切换并对齐播放位置)、AT_END(等当前状态播完再切); - advance_mode:
DISABLED(不使用)、ENABLED(仅在 travel 时使用)、AUTO(自动前进:条件与表达式为真时使用)——next()判断"是否有下一条路径"时,依据的正是 travel 路径与 AUTO 模式的自动前进; - priority:数值越低在 travel 与 AUTO 前进中越优先;
- reset:切换时目标动画是否从头播放,与
travel()的reset_on_teleport参数配合。
下面的图片展示了状态机编辑器的空初始界面(默认含Start与End两个内置状态),以及单个转场的属性面板——后者正是上面这些属性在编辑器中的落点:
完整的状态机搭建流程(创建状态、连接转场、配置三种转场类型与 advance 条件/表达式、travel()的使用前提)请参阅 Using AnimationTree 教程 的 StateMachine 与 StateMachine travel 两节。
九、综合实战示例:程序化控制角色状态机
结合上述 API,一个典型的"角色按下按键切换到对应动画"的控制器可以这样写:
extends CharacterBody3D @onready var animation_tree: AnimationTree = $AnimationTree # 缓存 playback 对象,避免每帧重复 get var state_machine: AnimationNodeStateMachinePlayback func _ready() -> void: state_machine = animation_tree["parameters/playback"] state_machine.state_started.connect(_on_state_started) state_machine.state_finished.connect(_on_state_finished) # 状态机必须先启动才能 travel state_machine.start("Idle") func _physics_process(_delta: float) -> void: # 通过 advance condition 参数驱动自动转场(编辑器里转场配置为 AUTO + 条件变量) animation_tree["parameters/conditions/is_walking"] = velocity.length() > 0.1 animation_tree["parameters/conditions/is_jumping"] = not is_on_floor() # 也可以随时强制 travel:走最短路径 if Input.is_action_just_pressed("dash"): state_machine.travel("Dash") func _on_state_started(state: StringName) -> void: print("开始播放: ", state) # 例如在 Run 开始时播放脚步循环音效 func _on_state_finished(state: StringName) -> void: print("播放结束: ", state) # 例如在 Attack 结束时清除攻击判定要点回顾:
travel()传目标状态名即可,中间状态由 A* 自动规划;- 无路径时会传送,
reset_on_teleport控制传送后是否重置动画; next()用于立刻执行已规划好的下一步;- 交叉淡入期间用
get_fading_from_node()等方法观测过渡源; - 信号注意 grouped 状态机的前缀透传与 crossfade 延迟触发语义。
十、速查表:方法、信号与继承
继承链:Object→RefCounted→Resource(resource_local_to_scene覆盖为true)
方法一览:
| 方法 | 签名 | 说明 |
|---|---|---|
start | (node: StringName, reset: bool = true) | 开始播放指定状态 |
stop | () | 停止当前播放 |
travel | (to_node: StringName, reset_on_teleport: bool = true) | 沿最短路径转场,无路径则传送 |
next | () | 立即切换到 travel/自动前进给出的下一状态 |
is_playing | () -> bool | 是否正在播放 |
get_current_node | () -> StringName | 当前状态名(淡入开始后即返回新状态) |
get_current_play_position | () -> float | 当前状态播放位置 |
get_current_length | () -> float | 当前状态长度(可能返回剩余长度) |
get_travel_path | () -> Array[StringName] | A* 计算出的旅行路径 |
get_fading_from_node | () -> StringName | 淡出源状态名 |
get_fading_from_length | () -> float | 淡出源状态长度 |
get_fading_from_play_position | () -> float | 淡出源状态播放位置 |
get_fading_length | () -> float | 当前淡入总时长 |
get_fading_position | () -> float | 当前淡入播放位置 |
信号一览:
| 信号 | 参数 | 说明 |
|---|---|---|
state_started | (state: StringName) | 状态开始播放;grouped 状态机信号带前缀透传 |
state_finished | (state: StringName) | 状态播放结束;有 crossfade 时在淡出源影响力消失后触发 |
延伸阅读
- Using AnimationTree 教程:状态机的搭建、转场类型、Advance Condition / Expression、travel 的完整讲解;
- AnimationNodeStateMachine 类参考:状态机本身的属性(
state_machine_type、allow_transition_to_self、reset_ends)与图操作方法; - AnimationNodeStateMachineTransition 类参考:转场的 xfade、switch mode、advance mode、priority 等参数细节;
- AnimationTree 类参考:
tree_root、anim_player、advance_expression_base_node等与 Playback 联动的基础配置。
- 文档
- 教程
- 游戏开发
【免费下载链接】godot-docs
Godot Engine official documentation
相关推荐
mesh2motion动画状态管理:播放控制与状态同步机制
mesh2motion动画状态管理:播放控制与状态同步机制 引言:3D动画状态管理的核心挑战 在3D角色动画系统中,动画状态管理(Animation State
前端3D渲染图形学laravel-paystack订阅支付实战:创建Plan、绑定客户并启用周期性扣费的完整教程
laravel paystack订阅支付实战:创建Plan、绑定客户并启用周期性扣费的完整教程 laravel paystack 是一款专为 Laravel 6
如何快速上手Featureform:从零开始的5步完整指南
如何快速上手Featureform:从零开始的5步完整指南 Featureform是一款虚拟特征存储工具,能够将您现有的数据基础设施转变为功能完善的特征存储。本
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考