Godot 4 GDScript 教程 / 动画系统(AnimationPlayer/Tree)
动画系统(AnimationPlayer/Tree)
概述
Godot 4 的动画系统强大且灵活,由 AnimationPlayer(关键帧动画播放器)和 AnimationTree(高级动画混合状态机)两大核心节点组成,支持 2D/3D 属性动画、骨骼动画、动画回调和程序化动画。
| 节点 | 说明 |
|---|---|
AnimationPlayer | 关键帧动画编辑/播放 |
AnimationTree | 状态机、混合树、过渡控制 |
AnimationMixer | Godot 4.3+ 动画混合基类 |
AnimationPlayer 基础
代码播放动画
extends CharacterBody3D
@onready var anim_player: AnimationPlayer = $AnimationPlayer
func play_idle():
anim_player.play("idle")
func play_run():
anim_player.play("run", 0.2) # 0.2 秒过渡
func play_attack():
anim_player.play("attack")
func _on_animation_finished(anim_name: StringName):
if anim_name == "attack":
anim_player.play("idle")
AnimationPlayer 属性轨道
AnimationPlayer 可以动画化几乎任何属性:
| 轨道类型 | 说明 | 示例 |
|---|---|---|
| 属性轨道 | Node 属性 | position, rotation, modulate:a |
| 方法调用轨道 | 调用脚本方法 | play_footstep_sound() |
| Bezier 轨道 | 曲线控制 | 复杂缓动 |
| 音频轨道 | 播放音效 | 攻击音效 |
| 子动画轨道 | 嵌套动画 | 组合动画 |
方法调用轨道
extends Node3D
@onready var anim_player: AnimationPlayer = $AnimationPlayer
func _ready():
# 监听动画中的方法调用
pass
# 在动画编辑器中添加方法调用轨道
# 在特定帧调用此方法
func spawn_attack_effect():
var effect = preload("res://effects/slash.tscn").instantiate()
effect.global_position = $WeaponTip.global_position
get_tree().current_scene.add_child(effect)
func play_slash_sound():
$SlashSound.play()
AnimationTree 改进
AnimationTree 提供状态机和混合树,是复杂动画系统的核心。
基本设置
extends CharacterBody3D
@onready var anim_tree: AnimationTree = $AnimationTree
func _ready():
anim_tree.active = true
状态机(StateMachine)
状态机适合管理离散的动画状态(Idle、Run、Jump 等)。
extends CharacterBody3D
@onready var anim_tree: AnimationTree = $AnimationTree
var state_machine: AnimationNodeStateMachinePlayback
func _ready():
anim_tree.active = true
state_machine = anim_tree["parameters/playback"]
func _physics_process(delta):
var h_speed = Vector2(velocity.x, velocity.z).length()
if not is_on_floor():
if velocity.y > 0:
state_machine.travel("Jump")
else:
state_machine.travel("Fall")
elif h_speed > 0.1:
state_machine.travel("Run")
else:
state_machine.travel("Idle")
# 触发一次性动画
if Input.is_action_just_pressed("attack"):
state_machine.travel("Attack")
| 状态机方法 | 说明 |
|---|---|
travel("状态名") | 过渡到目标状态(经过中间状态) |
start("状态名") | 立即切换到目标状态 |
get_current_node() | 获取当前状态名 |
is_playing() | 是否正在播放 |
混合树(BlendTree)
混合树适合连续的动画混合(方向混合、上半身/下半身分离等)。
方向混合
extends CharacterBody3D
@onready var anim_tree: AnimationTree = $AnimationTree
func _process(_delta):
# 更新混合参数
anim_tree["parameters/BlendSpace/blend_position"] = Vector2(
velocity.x / 5.0, # 左右 (-1 ~ 1)
velocity.z / 5.0 # 前后 (-1 ~ 1)
)
BlendSpace2D 结构
Forward (0, -1)
|
Left(-1, 0) --+-- Right(1, 0)
|
Back (0, 1)
在 AnimationTree 编辑器中设置 BlendSpace2D,在对应位置放置动画片段(Idle、RunForward、RunLeft、RunRight、RunBack 等),引擎自动混合。
动画过渡 AnimationNodeTransition
extends Node3D
@onready var anim_tree: AnimationTree = $AnimationTree
func set_grounded(grounded: bool):
# 在混合树中使用 Transition 节点切换状态
anim_tree["parameters/InAir/transition_request"] = "Grounded" if grounded else "Air"
func set_aiming(aiming: bool):
anim_tree["parameters/AimTransition/transition_request"] = "Aiming" if aiming else "Relaxed"
💡 提示:Godot 4.3+ 中 AnimationNodeTransition 替代了旧的 AnimationNodeOneShot,提供更平滑的过渡。
骨骼动画
骨骼修改器 SkeletonModifier3D
extends Node3D
@onready var skeleton: Skeleton3D = $Armature/Skeleton3D
# IK(反向运动学)让角色看向目标
@onready var ik: LookAtModifier3D = $Armature/Skeleton3D/LookAtModifier3D
var look_target: Vector3 = Vector3.ZERO
func _process(_delta):
ik.target_position = look_target
物理骨骼
extends Node3D
@onready var phys_bone: PhysicalBoneSimulator3D = $PhysicalBoneSimulator3D
func enable_ragdoll():
phys_bone.physical_bones_start_simulation()
func disable_ragdoll():
phys_bone.physical_bones_stop_simulation()
程序化动画
不使用预制动画,通过代码驱动骨骼:
extends Node3D
@onready var skeleton: Skeleton3D = $Armature/Skeleton3D
@export var head_bone: String = "Head"
@export var look_target: Vector3
func _process(_delta):
var bone_idx = skeleton.find_bone(head_bone)
if bone_idx == -1:
return
# 获取骨骼全局姿态
var bone_global = skeleton.get_bone_global_pose(bone_idx)
# 计算朝向
var dir = (look_target - global_position).normalized()
var target_rotation = Quaternion(bone_global.basis.looking_at(dir))
# 平滑旋转
bone_global.basis = Basis(bone_global.basis.get_rotation_quaternion().slerp(target_rotation, 0.1))
skeleton.set_bone_global_pose(bone_idx, bone_global)
动画复用与继承
动画资源导入设置
# 在导入时将动画拆分为多个片段
# FBX/glTF 文件可包含多个动画,导入设置中可设置循环等属性
# 代码中复用动画
func setup_animations():
# 从其他场景复制动画
var source_anim_player: AnimationPlayer = preload("res://animations/shared_anims.tscn").instantiate().get_node("AnimationPlayer")
var my_anim_player: AnimationPlayer = $AnimationPlayer
for anim_name in source_anim_player.get_animation_list():
var anim = source_anim_player.get_animation(anim_name)
my_anim_player.add_animation_library("shared", source_anim_player.get_animation_library("shared"))
AnimationLibrary
extends Node3D
@onready var anim_player: AnimationPlayer = $AnimationPlayer
func _ready():
# 从不同 Library 播放动画
anim_player.play("shared/idle") # 共享动画库
anim_player.play("custom/attack") # 自定义动画库
2D 帧动画
extends Sprite2D
@onready var anim_player: AnimationPlayer = $AnimationPlayer
# 使用 AnimatedSprite2D 更方便
# 或者用 AnimationPlayer 控制 frame 属性
# AnimatedSprite2D 方式
@onready var animated_sprite: AnimatedSprite2D = $AnimatedSprite2D
func play_walk(direction: String):
animated_sprite.play("walk_" + direction) # walk_up, walk_down, walk_left, walk_right
func play_idle():
animated_sprite.play("idle")
游戏开发场景
| 场景 | 推荐方案 |
|---|---|
| 角色基础动画 | AnimationTree 状态机 |
| 方向移动混合 | BlendSpace2D |
| 上下半身分离 | BlendTree + 分层 |
| 物理布娃娃 | PhysicalBoneSimulator3D |
| UI 动画 | AnimationPlayer + Control 属性 |
| 2D 帧动画 | AnimatedSprite2D |
| 过场动画 | AnimationPlayer + 相机轨道 |
⚠️ 常见陷阱
- AnimationTree 必须设
active = true才能工作 - 状态机
travel()不会立即切换,经过中间状态过渡 - 混合树参数类型要匹配,BlendSpace1D 用 float,BlendSpace2D 用 Vector2
- 骨骼动画的骨骼名区分大小写
- Godot 4.3+ 中 AnimationMixer 统一了 AnimationPlayer 和 AnimationTree 的底层逻辑