Godot 3 GDScript 教程 / Godot 3 GDScript 教程(十一):动画系统(AnimationPlayer)
动画系统(AnimationPlayer)
动画是游戏的灵魂。Godot 3 的 AnimationPlayer 节点提供了强大而灵活的动画系统,不仅能驱动属性变化,还能调用方法、触发事件,适用于 UI 过渡、角色动作、场景演出等几乎所有场景。
AnimationPlayer 基础
AnimationPlayer 是 Godot 动画系统的核心节点,可以对场景中的任意节点的任意属性进行动画控制。
基本控制
# 播放动画
$AnimationPlayer.play("idle")
# 播放并等待完成
yield($AnimationPlayer, "animation_finished")
# 停止动画
$AnimationPlayer.stop()
# 设置播放速度(负值为倒放)
$AnimationPlayer.playback_speed = 2.0
# 检查是否正在播放
if $AnimationPlayer.is_playing():
print("动画播放中: ", $AnimationPlayer.current_animation)
AnimationPlayer 重要属性
| 属性/方法 | 说明 |
|---|---|
play(name) | 播放指定名称的动画 |
stop(reset) | 停止动画,reset 决定是否回到起始 |
is_playing() | 判断当前是否在播放 |
current_animation | 当前动画名称 |
playback_speed | 播放速度倍率 |
animation_finished | 动画完成信号 |
get_animation_list() | 获取所有动画名称列表 |
💡 提示:playback_speed = -1.0 可以实现动画倒放,适合实现"时间回溯"等特效。
关键帧动画
关键帧(Keyframe)是动画的基本单位。通过在不同时间点设置属性值,引擎自动插值过渡。
插值模式
| 插值模式 | 说明 |
|---|---|
INTERPOLATION_NEAREST | 无插值,直接跳到下一帧 |
INTERPOLATION_LINEAR | 线性插值 |
INTERPOLATION_CUBIC | 三次插值,更平滑 |
代码动态创建关键帧
var animation = Animation.new()
# 创建位置属性轨道
var track_idx = animation.add_track(Animation.TYPE_VALUE)
animation.track_set_path(track_idx, "Sprite:position")
# 插入关键帧:时间、值
animation.track_insert_key(track_idx, 0.0, Vector2(0, 0))
animation.track_insert_key(track_idx, 0.5, Vector2(200, 0))
animation.track_insert_key(track_idx, 1.0, Vector2(200, 100))
animation.loop = true
$AnimationPlayer.add_animation("move_path", animation)
$AnimationPlayer.play("move_path")
属性动画
属性动画可以驱动节点的任何可序列化属性,如 position、modulate、scale、rotation 等。
缩放动画(受击反馈)
# 创建缩放"弹跳"动画用于受击反馈
func create_hit_animation() -> Animation:
var anim = Animation.new()
var track = anim.add_track(Animation.TYPE_VALUE)
anim.track_set_path(track, ".:scale")
anim.track_insert_key(track, 0.0, Vector2(1, 1))
anim.track_insert_key(track, 0.05, Vector2(1.3, 1.3))
anim.track_insert_key(track, 0.1, Vector2(0.9, 0.9))
anim.track_insert_key(track, 0.15, Vector2(1, 1))
anim.length = 0.15
return anim
⚠️ 注意:属性动画的 track_path 格式为 "NodeName:property" 或 ".:property"(当前节点),路径错误是最常见的动画不生效原因。
方法调用轨道
AnimationPlayer 不仅能动画属性,还能在指定时间点调用节点的方法。
var animation = Animation.new()
# 创建方法调用轨道
var track_idx = animation.add_track(Animation.TYPE_METHOD)
animation.track_set_path(track_idx, ".")
# 在 0.5 秒时调用 play_footstep_sound
animation.track_insert_key(track_idx, 0.5, {
"method": "play_footstep_sound",
"args": []
})
# 在 1.0 秒时调用带参数的方法
animation.track_insert_key(track_idx, 1.0, {
"method": "spawn_dust_effect",
"args": [Vector2(0, 32)]
})
$AnimationPlayer.add_animation("walk", animation)
func play_footstep_sound():
$FootstepSound.play()
func spawn_dust_effect(pos: Vector2):
var dust = dust_scene.instance()
dust.position = pos
add_child(dust)
💡 提示:方法调用轨道非常适合触发音效、粒子特效、伤害判定等事件,避免用代码手动计时。
动画过渡与混合
常见过渡策略
| 场景 | 推荐过渡时间 | 说明 |
|---|---|---|
| 站立 → 奔跑 | 0.1 ~ 0.2s | 快速响应 |
| 攻击 → 站立 | 0.2 ~ 0.3s | 略有收招感 |
| 受击 → 站立 | 0.15s | 硬直后恢复 |
| 待机切换 | 0.3 ~ 0.5s | 平滑自然 |
# 从当前动画平滑过渡到 "run"
$AnimationPlayer.play("run", -1, 1.0, false)
动画状态机
对于角色动画管理,状态机是最常用的模式。
代码控制状态切换
onready var anim_tree = $AnimationTree
onready var state_machine = anim_tree.get("parameters/playback")
func _physics_process(delta):
var velocity = get_velocity()
if not is_on_floor():
state_machine.travel("jump")
elif velocity.length() > 10:
state_machine.travel("run")
else:
state_machine.travel("idle")
# 攻击时直接切换
if Input.is_action_just_pressed("attack"):
state_machine.start("attack")
⚠️ 注意:travel() 会寻找最短路径过渡,而 start() 会立即切换。攻击等需要立即响应的动作建议用 start()。
AnimationTree(Godot 3.x)
AnimationTree 提供了比 AnimationPlayer 更高级的动画控制,支持状态机、混合树等。
BlendSpace2D 实现八方向移动
onready var anim_tree = $AnimationTree
func _ready():
anim_tree.active = true
func update_blend_space(velocity: Vector2):
var normalized = velocity.normalized()
anim_tree.set("parameters/MoveBlend/blend_position", normalized)
动画混合树(BlendTree)
在编辑器中创建 BlendTree:
1. AnimationTree → Tree Root → AnimationNodeBlendTree
2. 添加 Animation 节点(idle, run)
3. 添加 Blend2 节点,连接动画
4. 代码控制混合比例:
anim_tree.set("parameters/Blend2/blend_amount", run_weight)
动画事件触发
利用方法调用轨道,可以在动画的特定时刻触发游戏逻辑。
实现攻击判定窗口
# 在攻击动画中使用方法调用轨道
# 帧 5: enable_hitbox() 帧 10: disable_hitbox()
func enable_hitbox():
$AttackArea/CollisionShape2D.disabled = false
$AttackEffect.emitting = true
func disable_hitbox():
$AttackArea/CollisionShape2D.disabled = true
func _on_AttackArea_body_entered(body):
if body.has_method("take_damage"):
body.take_damage(attack_power)
骨骼动画
2D 骨骼(Bone2D)
# Godot 3 的 Skeleton2D + Bone2D 系统
# 代码控制骨骼
$Skeleton2D/Bone2D/UpperArm.rotation_degrees = 45
$Skeleton2D/Bone2D/UpperArm/LowerArm.rotation_degrees = -30
3D 骨骼动画
onready var skeleton = $Armature/Skeleton
onready var anim_player = $AnimationPlayer
func play_attack():
anim_player.play("attack")
yield(anim_player, "animation_finished")
anim_player.play("idle")
动画复用
方法一:共享动画资源
# 多个同类型角色共享动画
var shared_animations = preload("res://animations/enemy_animations.tres")
func _ready():
$AnimationPlayer.add_animation_library(shared_animations)
方法二:代码动画模板
# 创建可复用的动画工厂
class_name AnimationFactory
static func create_tween_bounce(node: Node, property: String, amount: float, duration: float = 0.15) -> Animation:
var anim = Animation.new()
var track = anim.add_track(Animation.TYPE_VALUE)
anim.track_set_path(track, node.name + ":" + property)
var original = node.get(property)
anim.track_insert_key(track, 0.0, original)
anim.track_insert_key(track, duration * 0.3, original * (1.0 + amount))
anim.track_insert_key(track, duration, original)
anim.length = duration
return anim
⚠️ 注意:共享动画时,确保动画轨道的 track_path 与各角色的节点结构一致,否则动画将无法正确驱动目标属性。
游戏开发场景
场景一:UI 界面过渡动画
func show_menu():
$AnimationPlayer.play("menu_fade_in")
yield($AnimationPlayer, "animation_finished")
$MenuContainer.visible = true
func hide_menu():
$AnimationPlayer.play("menu_fade_out")
yield($AnimationPlayer, "animation_finished")
$MenuContainer.visible = false
场景二:过场演出(Cutscene)
func play_cutscene():
get_tree().paused = true
$CutscenePlayer.play("boss_intro")
yield($CutscenePlayer, "animation_finished")
get_tree().paused = false
emit_signal("cutscene_finished")
扩展阅读
💡 总结:AnimationPlayer 是 Godot 动画系统的核心,善用属性动画 + 方法调用轨道可以覆盖绝大多数游戏动画需求。对于复杂的角色动画,推荐使用 AnimationTree + StateMachine 的组合。