☰
Godot 引擎 AnimationTree 全面指南:用动画树与状态机实现高级动画过渡
2026/10/3 8:39:12 网站建设 项目流程
  • 文档
  • 教程
  • 游戏开发

【免费下载链接】godot-docs

Godot Engine official documentation

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

导读

AnimationTree 是 Godot 引擎中用于高级动画过渡与混合的核心节点。它本身不存储动画,而是托管在 AnimationPlayer 中的动画资源,通过可视化的节点图、混合空间与状态机来完成单靠 AnimationPlayer 无法实现的复杂过渡(如角色移动状态切换、攻击一击动画、朝向混合等)。阅读本文后,你将掌握 AnimationTree 的架构与根节点体系、混合树与状态机的搭建方法、Advance Expression 进阶条件表达式、BlendSpace1D/2D 混合空间、根运动(Root Motion)提取,以及从代码实时操控动画参数的全部技能。

AnimationTree 是什么:为什么需要它

Godot 的 AnimationPlayer 本身已经拥有相当灵活的动画系统:它可以动画化几乎所有节点与资源的属性,并支持专用变换轨道、贝塞尔曲线轨道、函数调用轨道、音频轨道与子动画轨道。然而,AnimationPlayer 对动画混合的支持是有限的——它只能设置一个固定的交叉淡入淡出(cross-fade)时间。

AnimationTree 正是为处理这类高级过渡而设计的节点。它的类继承关系为:

AnimationTree ← AnimationMixer ← Node ← Object

AnimationTree 与 AnimationMixer 构成继承链——AnimationMixer 是 AnimationPlayer 与 AnimationTree 的公共基类,负责管理动画库列表,并提供播放、混合相关的通用属性与方法。AnimationTree 在 AnimationMixer 之上构建了"树形/图形化"的混合逻辑:实例化播放数据后,实际的混合计算由 AnimationMixer 完成。

AnimationTree 与 AnimationPlayer 的分工

AnimationTree 节点不包含自己的动画。动画的创建、编辑与导入都在 AnimationPlayer 中完成,AnimationTree 只负责控制播放与过渡。这一点在官方教程 Using AnimationTree 中说得非常明确:

  • AnimationPlayer:仅用于添加、删除、编辑动画;
  • AnimationTree 及其组成的 AnimationNode:负责播放与过渡的控制。

注意:当 AnimationTree 与 AnimationPlayer 关联后,对应 AnimationPlayer 的若干属性与方法将不再按预期工作。播放和过渡应只通过 AnimationTree 及其 AnimationNode 子节点处理。

典型的接入方式:AnimationTree 与 AnimationPlayer 可同时用于 2D 与 3D 场景。导入 3D 场景时,动画会进入一个 AnimationPlayer 节点;由于你几乎不会直接使用导入的场景(通常会实例化或继承它),正确做法是在新场景中放置 AnimationTree 节点,然后把它的anim_player指向导入场景中的 AnimationPlayer。官方 TPS(第三人称射击)演示正是这样组织的。

核心属性:anim_player 与 tree_root

属性类型默认值说明
anim_playerNodePathNodePath("")用于播放动画的 AnimationPlayer 节点路径,对应set_animation_player()/get_animation_player()
tree_rootAnimationRootNode—本 AnimationTree 的根动画节点,对应set_tree_root()/get_tree_root()
advance_expression_base_nodeNodePathNodePath(".")当内部未显式指定表达式求值节点时,用于求值 AnimationNode Expression 的节点路径
deterministicbooltrue(覆盖 AnimationMixer 的false)确定性混合开关
callback_mode_discreteAnimationCallbackModeDiscrete2(覆盖 AnimationMixer 的默认值)离散回调模式

改变anim_player时会发出animation_player_changed信号,可用于在代码中感知关联关系的变化。

创建动画树:根节点与三种子节点

要使用 AnimationTree,必须先设置一个根节点。动画根节点(AnimationRootNode)是一个包含并求值子节点、最终输出动画的类。其下有三类子节点:

  1. Animation 节点:引用关联 AnimationPlayer 中的一段动画;
  2. Animation Root 节点:用于混合子节点,可以嵌套;
  3. Animation Blend 节点:用在 AnimationNodeBlendTree(一个二维节点图)中,接受多个输入端口、给出一个输出端口。

可用的根节点类型有以下几种:

  • AnimationNodeAnimation:从列表中选择一段动画并播放。这是最简单的根节点,通常不直接作为根使用;
  • AnimationNodeBlendTree:以图的形式容纳多个子节点,提供 mix、blend2、blend3、one shot 等多种混合节点;
  • AnimationNodeBlendSpace1D:在 1D 混合空间内对两个动画节点做线性混合,通过控制混合位置在动画间过渡;
  • AnimationNodeBlendSpace2D:在 2D 混合空间内对三个动画节点做线性混合,通过 2D 混合位置混合动画;
  • AnimationNodeStateMachine:以图的形式容纳多个节点,每个节点作为一个状态,通过多种函数在状态间切换。

混合树(Blend Tree)

创建 AnimationNodeBlendTree 后,底部面板的AnimationTree 标签页中会出现一个空白的 2D 图,默认只包含一个Output节点。要让动画播放,必须把某个节点连接到 Output——最简单的方式是直接把一个Animation节点连到 Output 上,实现单纯的动画回放。

节点可通过Add Node..菜单添加,或右键空白处添加。下面逐个说明常用节点:

Blend2 / Blend3

这两个节点根据用户指定的混合值,在 2 个或 3 个输入之间混合。混合时可以使用**过滤器(filters)**单独控制哪些轨道参与混合、哪些不参与——这对"在已有动画之上叠加另一层动画"非常有用。更复杂的混合建议改用混合空间(BlendSpace)。

OneShot

该节点执行一次子动画,完成后返回。可自定义淡入/淡出混合时间以及过滤器。触发方式通过parameters/OneShot/request参数完成,请求值对应 AnimationNodeOneShot 的OneShotRequest枚举:

请求值含义
ONE_SHOT_REQUEST_NONE = 0默认状态,不执行任何操作
ONE_SHOT_REQUEST_FIRE = 1触发"shot"端口连接的动画播放
ONE_SHOT_REQUEST_ABORT = 2中止"shot"端口连接的动画
ONE_SHOT_REQUEST_FADE_OUT = 3对"shot"端口动画执行淡出后中止

在设置请求并改变播放后,one-shot 节点会在下一个处理帧自动把request清回ONE_SHOT_REQUEST_NONE。OneShot 还支持autorestart(自动重播)、autorestart_delay(默认1.0秒)、autorestart_random_delay、fade_in_time/fade_out_time等参数。

# 播放连接到 "shot" 端口的子动画。 animation_tree.set("parameters/OneShot/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE) # 等价写法: animation_tree["parameters/OneShot/request"] = AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE # 中止子动画。 animation_tree["parameters/OneShot/request"] = AnimationNodeOneShot.ONE_SHOT_REQUEST_ABORT # 带淡出中止子动画。 animation_tree["parameters/OneShot/request"] = AnimationNodeOneShot.ONE_SHOT_REQUEST_FADE_OUT # 获取当前状态(只读)。 animation_tree.get("parameters/OneShot/active")

TimeSeek

该节点允许把in输入连接的动画跳转到指定时间点再播放,适合"从某个播放位置开始播放动画"的场景。注意seek_request值以秒为单位:想从头播放设为0.0,想从 3 秒处开始播放则设为3.0:

# 从头播放子动画。 animation_tree.set("parameters/TimeSeek/seek_request", 0.0) # 从 12 秒时间戳开始播放。 animation_tree["parameters/TimeSeek/seek_request"] = 12.0

TimeScale

该节点缩放in输入动画的播放速度——速度会乘以scale参数的值:设为0.0暂停动画,设为负数则倒放动画:

animation_tree["parameters/TimeScale/scale"] = 0.5 # 半速播放 animation_tree["parameters/TimeScale/scale"] = -1.0 # 倒放

Transition

这是 StateMachine 的简化版:把动画连接到各输入端,current_state索引决定播放哪一段动画,并可指定交叉淡化过渡时间。在检查器中可增减输入端口数量、重排或删除输入:

# 播放连接到 "state_2" 端口的子动画。 animation_tree.set("parameters/Transition/transition_request", "state_2") # 获取当前状态名(只读)。 animation_tree.get("parameters/Transition/current_state") # 获取当前状态索引(只读)。 animation_tree.get("parameters/Transition/current_index")

状态机(StateMachine)

创建 AnimationNodeStateMachine 后,底部面板默认包含一个Start状态和一个End状态。添加状态可通过右键或工具栏的"新建节点"按钮完成;状态可以是动画、混合空间、混合树,甚至是另一个状态机(支持嵌套)。点击状态右侧的铅笔图标可进入该子节点进行编辑,点击面板左上角的Root返回原状态机。

状态之间必须用**过渡(transition)**连接才能发挥作用:点击工具栏的"连接节点"按钮,从一个状态拖到另一个状态。两个状态之间可以建立两条过渡,分别对应两个方向。

三种过渡类型

  • Immediate(立即):立即切换到下一状态;
  • Sync(同步):立即切换,但会把新状态对齐到旧状态的播放位置(seek 到旧状态的时间点);
  • At End(结尾):等待当前状态播放完毕,再切换到下一状态动画的开头。

过渡属性

点击某条过渡,检查器中会显示它的属性:

  • Xfade Time:状态间交叉淡化的时间;
  • Xfade Curve:使用曲线而非线性方式进行交叉淡化;
  • Reset:切换进入的目标状态是否从头播放(true)还是接着播(false);
  • Priority:与代码中的travel()配合使用,数值越低的过渡在路径旅行时越优先被选用;
  • Switch Mode:过渡类型(见上),创建后仍可在此修改;
  • Advance Mode:推进模式——Disabled表示不使用该过渡;Enabled表示仅在travel()期间使用;Auto表示当推进条件与推进表达式为真(或没有设置条件/表达式)时自动使用。

Advance Condition 与 Advance Expression

当 Advance Mode 为Auto时,最后两个属性决定过渡是否推进:

  • Advance Condition(推进条件):一个 true/false 检查。填入自定义变量名后,状态机走到该过渡时会检查变量是否为true,为真则继续。它只能检查"为真",无法检查"为假"——因此能力非常有限:如果你想基于一个属性来回切换,就必须建两个值相反的变量,分别检查它们是否为真。这正是 Godot 4 引入 Advance Expression 的原因。
  • Advance Expression(推进表达式):与条件类似,但不是检查单个变量是否为真,而是求值任意表达式——任何能写进if语句的东西都可以。合法的例子包括:
is_walking is_walking == true # 与上一条行为一致 is_walking && !is_idle velocity > 0 player.is_on_floor()

警告:表达式是区分大小写的。引用引擎属性(如 CharacterBody3D 上的velocity)时请使用snake_case命名规范;引用脚本属性时则需匹配脚本中的风格——GDScript 通常是snake_case,C# 通常是PascalCase。

Advance Expression 默认基于advance_expression_base_node属性指向的节点求值(默认是 AnimationTree 自身),实际使用时通常要把它改指到存放动画变量脚本的那个节点。该表达式通过 Godot 的 Expression 类求值。

状态机旅行(travel)

Godot 状态机的一大特色是travel(旅行):可以指示状态图从当前状态前往目标状态,途中顺路访问所有中间状态,路径计算由A* 算法完成。如果当前状态到目标状态之间不存在过渡路径,则会直接**传送(teleport)**到目标状态。

使用 travel 前,先从 AnimationTree 节点取出 AnimationNodeStateMachinePlayback 对象(以属性形式导出),再调用它的方法:

var state_machine = animation_tree["parameters/playback"] state_machine.travel("SomeState")

travel(to_node, reset_on_teleport = true)接受一个目标状态名,可控制传送时是否重置。相关方法还包括start()、stop()、next()(若存在由 travel 或自动推进产生的下一条路径,立即从当前状态切换到下一状态)、get_travel_path()(返回 A* 内部计算出的当前旅行路径)。注意:状态机必须处于运行状态才能 travel——要么调用start(),要么把某个节点连接到Start。

混合空间:BlendSpace1D 与 BlendSpace2D

BlendSpace2D在二维空间内做高级混合:把代表动画的点添加到 2D 空间中,再控制一个位置来决定动画间的混合权重。添加点可以右键点击图面,或使用工具栏的"添加点"按钮;点以所选动画或根节点类型命名,可点击名称重命名、拖拽名称或点来重新定位。开启Auto Triangles后,会自动使用Delaunay 三角剖分在插入点之间生成混合三角形。在编辑器中,按住 Shift 拖拽左键(或使用专用工具)即可操纵混合目标位置。

BlendSpace1D与 BlendSpace2D 原理相同,但只在一个维度(水平线)上混合。由于不涉及三角形,它可以少于三个混合点也能工作。

同步模式(Sync Mode)

BlendSpace1D 与 BlendSpace2D 的Sync Mode属性取代了旧的布尔sync属性,控制混合时动画的推进方式,共有四种模式:

模式行为
None(默认)非活动动画冻结不推进,只有当前权重最高的活动动画前进
Independent非活动动画以权重0继续推进,等同于旧的sync = true行为
Cyclic Mutable所有动画按时间缩放以保持相位对齐;共享周期长度由当前混合权重动态计算,单一未混合动画仍以正常速度播放。适用于逻辑周期相同(如移动循环)但长度略有差异的动画
Cyclic Constant所有动画被缩放到恰好cyclic_length秒内完成一个完整周期,与各自原始长度无关;需将cyclic_length设为期望周期时长(必须大于0)

警告:循环同步模式要求所有混合点使用 AnimationNodeAnimation 且动画长度有限、不可变。若任何混合点使用其他节点类型,会显示警告且循环同步不生效。

提示:使用任一循环模式且各动画长度不同时,对输出应用 AnimationNodeTimeSeek 会破坏同步;此时应使用AnimationNodeAnimation.use_custom_timeline先归一化动画长度。

混合模式(Blend Mode)

默认混合发生在Continuous(连续)模式:BlendSpace2D 在最近三角形内插值、BlendSpace1D 在点连线上插值。但对逐帧 2D 动画,你可能想切换到Discrete(离散)模式,让混合的中间状态不出现;而Carry(携带)模式则允许在切换离散动画时保留当前播放位置。这些模式可通过Blend菜单设置。

获得更好的混合效果:确定性混合与 RESET

要让混合结果可复现且始终一致(确定性),被混合的属性值必须具有特定的初始值。例如,混合两段动画时,若一段动画有某属性轨道、另一段没有,那么缺失轨道的那段会按拥有该属性轨道且值为初始值来计算。

  • 对于 Skeleton3D 骨骼的 Position/Rotation/Scale 3D 轨道,初始值是Bone Rest(骨骼静止姿态);
  • 对于其他属性,初始值是0;若该轨道存在于RESET动画中,则使用其第一个关键帧的值。

举例:一个 AnimationPlayer 有两条动画,其中一条缺少 Position 属性轨道,那么这条动画的 Position 会被当作Vector2(0, 0)处理,导致混合结果异常。解决办法是在RESET动画中为 Position 添加一个初始值属性轨道。

注意:RESET动画的存在是为了定义对象加载时的默认姿态,通常只有一帧,不期望在时间线上被实际播放。

另一个要点:Rotation 3D 轨道以及插值类型为 Linear Angle / Cubic Angle 的 2D 旋转属性轨道,会阻止混合动画从初始值旋转超过 180 度。这对 Skeleton3D 防止混合时骨骼穿透身体很有用——因此 Skeleton3D 的 Bone Rest 值应尽可能接近可活动范围的中点,人类模型最好以 T-pose 导入,这样混合时会优先走 Bone Rest 到目标的最短旋转路径。若确实需要让 Skeleton3D 本身通过混合动画旋转超过 180 度,请使用根运动(Root Motion)。

根运动(Root Motion)

3D 动画制作中常用技巧是让动画师使用根骨骼带动其余骨骼运动,这样角色步伐能与地面精确吻合,过场动画中也能与物体精准交互。在 Godot 中回放动画时,可以把这根骨骼选为root motion track,从而在视觉上取消该骨骼的变换(动画在原地播放),随后通过 AnimationTree API 以变换形式取回实际运动量:

# 获取运动增量。 animation_tree.get_root_motion_position() animation_tree.get_root_motion_rotation() animation_tree.get_root_motion_scale() # 获取动画的实际混合值。 animation_tree.get_root_motion_position_accumulator() animation_tree.get_root_motion_rotation_accumulator() animation_tree.get_root_motion_scale_accumulator()

这些值可以喂给 CharacterBody3D.move_and_slide 之类的函数来控制角色移动。相关属性(继承自 AnimationMixer)包括root_motion_track、root_motion_local与root_node。另外还有一个工具节点RootMotionView,可以放置一个场景作为角色与动画的自定义地面(游戏运行时默认禁用)。

从代码控制 AnimationTree

搭建并预览好动画树后,剩下的问题就是"如何用代码控制这一切"。关键认知:动画节点只是资源(Resource),所有使用该 AnimationTree 的实例会共享它们。直接修改节点上的值会影响场景的所有实例——这通常不是我们想要的,但也有实用场景:比如复制粘贴动画树的部分内容,或在不同动画树间复用布局复杂的节点(如状态机、混合空间)。

真正的动画数据保存在 AnimationTree 节点中,通过属性访问。查看 AnimationTree 节点的Parameters部分,可以看到所有可实时修改的参数——它们甚至可以用另一个 AnimationPlayer 甚至 AnimationTree 自身来动画化,从而构建非常复杂的动画逻辑。

要修改这些值,必须先拿到属性路径:在检查器中将鼠标悬停在任一参数上即可看到路径,然后在代码中读写:

animation_tree.set("parameters/eye_blend/blend_amount", 1.0) # 等价写法: animation_tree["parameters/eye_blend/blend_amount"] = 1.0

提示:状态机中的 Advance Expression不会出现在 Parameters 下,因为它们存放在另一个脚本而非 AnimationTree 自身中;而 Advance Condition 可以在 Parameters 下找到。

处理回调模式(已弃用的枚举与迁移路径)

AnimationTree 自带一个AnimationProcessCallback枚举:

常量值说明
ANIMATION_PROCESS_PHYSICS0在物理帧中处理动画
ANIMATION_PROCESS_IDLE1在处理帧中更新动画
ANIMATION_PROCESS_MANUAL2不自动处理,使用advance()手动推进

以上三个常量均已弃用,请改用 AnimationMixer 的callback_mode_process属性及其AnimationCallbackModeProcess枚举:

  • ANIMATION_CALLBACK_MODE_PROCESS_PHYSICS = 0:在物理帧处理(对应NOTIFICATION_INTERNAL_PHYSICS_PROCESS),尤其适合动画化物理体;
  • ANIMATION_CALLBACK_MODE_PROCESS_IDLE = 1:在处理帧处理(对应NOTIFICATION_INTERNAL_PROCESS),默认值;
  • ANIMATION_CALLBACK_MODE_PROCESS_MANUAL = 2:不自动处理,需调用 advance() 手动推进。

同样,AnimationTree 的get_process_callback()/set_process_callback(mode)两个方法也已弃用,应改用AnimationMixer.callback_mode_process。与混合器生命周期相关的信号(如animation_started、animation_finished、mixer_applied、mixer_updated、caches_cleared)也继承自 AnimationMixer,可供监听。

小结

AnimationTree 将 Godot 动画系统从"单段播放 + 固定交叉淡化"提升到完整的可视化混合/状态机架构:

  • 架构上:AnimationTree(继承自 AnimationMixer)只做播放控制,动画资产仍归 AnimationPlayer 管理,二者职责清晰;
  • 混合能力:BlendTree(Blend2/3、OneShot、TimeSeek、TimeScale、Transition)、BlendSpace1D/2D(含四种 Sync Mode 与三种 Blend Mode)覆盖从简单交叉淡化到多维姿态混合的全部需求;
  • 状态管理:StateMachine 提供 Immediate/Sync/At End 三类过渡、Advance Condition/Expression 自动推进,以及基于 A* 的 travel 路径旅行;
  • 实战细节:确定性混合要求关注 RESET 动画与 Bone Rest 初始值,根运动可分离动画位移与渲染姿态;
  • 代码控制:所有可调参数集中在parameters/...属性路径下,支持运行时读写甚至被其他动画驱动。

掌握这套体系,你便能在 Godot 中构建出角色控制器、打击反馈、混合移动等专业级动画逻辑。若想深入了解各类节点的全部属性与参数,可继续查阅 AnimationNodeBlendTree、AnimationNodeOneShot、AnimationNodeStateMachinePlayback 等类的参考文档,以及官方完整教程 Using AnimationTree。

  • 文档
  • 教程
  • 游戏开发

【免费下载链接】godot-docs

Godot Engine official documentation

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

相关推荐

上一篇:OpenViking API 自动化测试套件全解析:从本地一键测试到 CI/CD 集成
下一篇:Kilo 项目基于 AI Agent 的自动化 Changelog 生成工作流:从 commit 到发布说明

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询