AnimationMixer
2026/4/14大约 5 分钟
最后同步日期:2026-04-15 | Godot 官方原文 — AnimationMixer
AnimationMixer
节点继承关系
继承链:Node -> AnimationMixer -> AnimationPlayer / AnimationTree
继承自 Node
| 类型 | 名称 | 说明 |
|---|---|---|
| 属性 | Name | 节点名称 |
| 属性 | ProcessMode | 处理模式(始终 / 暂停时 / 仅编辑器) |
| 属性 | ProcessPriority | 处理优先级,数字越小越先执行 |
| 信号 | ready | 节点进入场景树并准备就绪 |
| 信号 | tree_entered | 节点进入场景树 |
| 信号 | tree_exited | 节点完全离开场景树 |
| 方法 | GetNode<T>() | 按路径获取子节点 |
| 方法 | AddChild() | 添加子节点 |
| 方法 | RemoveChild() | 移除子节点 |
| 方法 | QueueFree() | 帧结束后释放节点 |
| 方法 | GetParent() | 获取父节点 |
定义
AnimationMixer 是 AnimationPlayer 和 AnimationTree 的共同父类。它负责管理动画列表(动画库)和混合处理逻辑,但不直接用于播放动画。
想象 AnimationMixer 是一个"遥控器的内部主板"——你不会直接跟主板打交道,而是通过遥控器上的按钮(AnimationPlayer 的 Play()、Pause())来操作。主板(AnimationMixer)默默地在底层处理所有动画数据的存储和混合计算。
一句话理解:AnimationMixer 是动画系统的"幕后管家",负责存储动画资源和处理混合逻辑,具体怎么播放由它的子类 AnimationPlayer 和 AnimationTree 来决定。
使用频率:★★★★★ 通用高频——你通常不会直接使用 AnimationMixer 节点,而是使用它的子类。了解它有助于理解 Godot 动画系统的底层架构。
节点用途
- 作为 AnimationPlayer 和 AnimationTree 的公共基类,提供共享的动画管理功能
- 管理动画库(AnimationLibrary)的添加、删除和重命名
- 处理动画混合(blending)的核心计算逻辑
- 提供根运动(Root Motion)数据提取接口
使用场景
典型场景
- 理解 AnimationPlayer 和 AnimationTree 的共同行为时(比如两者都有的信号、属性)
- 需要在代码中同时操作 AnimationPlayer 和 AnimationTree 时,可以用 AnimationMixer 类型引用
不适用场景
- 直接播放动画 -> 使用 AnimationPlayer
- 角色动作状态管理 -> 使用 AnimationTree
常用节点搭配
| 搭配节点 | 用途 | 必需? |
|---|---|---|
| AnimationPlayer | 作为 AnimationMixer 的子类,用于播放动画 | 子类使用 |
| AnimationTree | 作为 AnimationMixer 的子类,用于动画状态机(Animation State Machine) | 子类使用 |
生效必备素材/资源
| 资源 | 类型 | 说明 |
|---|---|---|
| AnimationLibrary | AnimationLibrary 资源 | 存储动画列表的容器,默认有一个空字符串命名的全局库 |
| Animation | Animation 资源 | 具体的动画数据,包含关键帧轨道信息 |
节点属性与信号
基础属性
| 属性 | 类型 | 默认值 | 继承自 | 说明 |
|---|---|---|---|---|
Active | bool | true | — | 是否启用动画处理。设为 false 时,动画不会更新 |
RootNode | NodePath | .. | — | 动画轨道路径的起始节点,默认是父节点 |
ResetOnSave | bool | true | — | 保存场景时是否应用 RESET 动画(编辑器用) |
Deterministic | bool | false | — | 是否使用确定性混合算法(影响混合权重归一化方式) |
音频与回调
| 属性 | 类型 | 默认值 | 继承自 | 说明 |
|---|---|---|---|---|
AudioMaxPolyphony | int | 32 | — | 每个音频轨道同时播放的最大声音数 |
CallbackModeProcess | 枚举 | Idle | — | 动画在哪个处理阶段更新:Physics(物理帧)/ Idle(渲染帧)/ Manual(手动调用 advance) |
CallbackModeMethod | 枚举 | Deferred | — | 方法轨道的调用方式:Deferred(延迟调用,更安全)/ Immediate(立即调用) |
根运动
| 属性 | 类型 | 默认值 | 继承自 | 说明 |
|---|---|---|---|---|
RootMotionTrack | NodePath | "" | — | 用于提取根运动的轨道路径。设置后,该轨道的变换会被提取出来供代码使用 |
RootMotionLocal | bool | false | — | 是否以本地空间提取根运动位置 |
信号
| 信号 | 触发时机 | 继承自 | 说明 |
|---|---|---|---|
AnimationFinished | 动画播放完毕 | — | 参数是动画名称。注意:循环动画不会触发此信号 |
AnimationStarted | 动画开始播放 | — | 参数是动画名称 |
AnimationListChanged | 动画列表发生变化 | — | 添加或删除动画时触发 |
AnimationLibrariesUpdated | 动画库发生变化 | — | 动画库增删改时触发 |
CachesCleared | 缓存被清除 | — | 调用 ClearCaches() 或自动清理时触发 |
常用方法
| 方法 | 返回值 | 说明 |
|---|---|---|
GetAnimationList() | string[] | 获取所有动画名称列表 |
GetAnimation(name) | Animation | 按名称获取动画资源 |
HasAnimation(name) | bool | 是否存在指定名称的动画 |
FindAnimation(animation) | StringName | 反向查找动画资源的名称 |
GetAnimationLibraryList() | string[] | 获取所有动画库名称 |
GetAnimationLibrary(name) | AnimationLibrary | 获取指定名称的动画库 |
AddAnimationLibrary(name, library) | Error | 添加动画库 |
RemoveAnimationLibrary(name) | void | 移除动画库 |
RenameAnimationLibrary(name, newName) | void | 重命名动画库 |
Advance(delta) | void | 手动推进动画(配合 Manual 模式使用) |
ClearCaches() | void | 清除动画节点缓存(节点被删除后调用) |
GetRootMotionPosition() | Vector3 | 获取根运动位置增量 |
GetRootMotionRotation() | Quaternion | 获取根运动旋转增量 |
GetRootMotionScale() | Vector3 | 获取根运动缩放增量 |
代码示例
获取所有动画列表
C
using Godot;
public partial class AnimationInspector : Node
{
private AnimationMixer _mixer;
public override void _Ready()
{
// 获取场景中的 AnimationPlayer(它是 AnimationMixer 的子类)
_mixer = GetNode<AnimationMixer>("AnimationPlayer");
// 列出所有动画
var animList = _mixer.GetAnimationList();
foreach (var animName in animList)
{
GD.Print($"动画: {animName}");
}
// 获取并播放第一个动画
if (animList.Length > 0)
{
var anim = _mixer.GetAnimation(animList[0]);
GD.Print($"第一个动画长度: {anim.Length} 秒");
}
}
}GDScript
extends Node
@onready var mixer: AnimationMixer = $AnimationPlayer
func _ready():
# 列出所有动画
var anim_list = mixer.get_animation_list()
for anim_name in anim_list:
print("动画: ", anim_name)
# 获取并播放第一个动画
if anim_list.size() > 0:
var anim = mixer.get_animation(anim_list[0])
print("第一个动画长度: ", anim.length, " 秒")使用手动推进模式
C
using Godot;
public partial class ManualAnimation : Node
{
private AnimationMixer _mixer;
private double _accumulator;
public override void _Ready()
{
_mixer = GetNode<AnimationMixer>("AnimationPlayer");
// 设置为手动模式,动画不会自动推进
_mixer.CallbackModeProcess = AnimationMixer.AnimationCallbackModeProcessEnum.Manual;
}
public override void _Process(double delta)
{
// 自定义推进速度:游戏暂停时动画也暂停
if (!GetTree().Paused)
{
_mixer.Advance((float)delta);
}
}
}GDScript
extends Node
@onready var mixer: AnimationMixer = $AnimationPlayer
func _ready():
# 设置为手动模式,动画不会自动推进
mixer.callback_mode_process = AnimationMixer.ANIMATION_CALLBACK_MODE_PROCESS_MANUAL
func _process(delta):
# 自定义推进速度:游戏暂停时动画也暂停
if not get_tree().paused:
mixer.advance(delta)什么时候用 AnimationMixer?
大部分情况下你直接使用 AnimationPlayer 或 AnimationTree 就够了。当你需要编写通用代码,同时兼容 AnimationPlayer 和 AnimationTree 时,才需要用 AnimationMixer 类型来引用它们。
