拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Godot 动画状态机播放控制器 AnimationNodeStateMachinePlayback 完全指南

Godot 动画状态机播放控制器 AnimationNodeStateMachinePlayback 完全指南
  • 文档
  • 教程
  • 游戏开发

【免费下载链接】godot-docs

Godot Engine official documentation

项目地址:https://gitcode.com/GitHub_Trending/go/godot-docs
点击查看免费下载

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."

也就是说:

  1. 有路径:Godot 用A* 算法在状态图中计算从当前状态到目标状态的最短路径,并依次"途经"所有中间状态,每个中间转场按照各自配置的 xfade 时间、switch mode 执行;
  2. 无路径:图直接传送到目标状态(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

项目地址:https://gitcode.com/GitHub_Trending/go/godot-docs
点击查看免费下载
上一篇:CVAT完整指南:如何挑选免费的开源图像视频标注工具
下一篇:如何彻底解决Dell G15散热问题:tcc-g15开源控制中心完整指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

返回列表