Godot 4 AnimatedSprite2D 实战:让待机与移动动画自动切换

发布时间:2026/7/28 17:23:58
Godot 4 AnimatedSprite2D 实战:让待机与移动动画自动切换 Godot 4 AnimatedSprite2D 实战让待机与移动动画自动切换角色已经能移动但如果始终显示同一张图片游戏仍会显得像在拖动贴纸。AnimatedSprite2D可以用一个SpriteFrames资源管理多组逐帧动画再根据角色的真实速度切换idle和walk。这篇文章承接普通的CharacterBody2D八方向移动脚本完成素材导入、动画分组、运行时切换、方向翻转和一次性动画恢复。示例保持小而完整适合直接放进 Godot 4 的 2D 项目。一、Sprite2D 和 AnimatedSprite2D 的区别Sprite2D主要显示单张纹理适合静态物体或由代码单独控制纹理的场景。AnimatedSprite2D使用SpriteFrames保存多组逐帧动画。一个节点中可以同时拥有idle待机循环walk行走循环yawn打哈欠等一次性动作后续还可以加入攻击、跳跃和受伤动画。不同动作不必拆成多个显示节点。运行时只需要调用play(动画名)。二、准备角色节点角色场景建议保持以下结构Player (CharacterBody2D) ├── AnimatedSprite2D └── CollisionShape2D选中AnimatedSprite2D在 Inspector 的Sprite Frames属性中选择New SpriteFrames再打开底部的 SpriteFrames 编辑器。新建资源默认内置在当前场景中。如果多个角色需要共享同一套动画可以再将它保存为独立的.tres文件。三、建立 idle 与 walk 动画在 SpriteFrames 编辑器中把默认动画重命名为idle添加待机帧打开循环设置与素材匹配的 FPS新建walk添加行走帧并打开循环。FPS 表示每秒播放多少帧不会凭空增加原始画面。帧数较少时把 FPS 调得过高动作反而会显得跳跃。应直接在编辑器中预览以素材表达的节奏为准。把 Autoplay 设为idle运行场景后角色即使没有脚本切换也会先播放待机动画。四、用真实速度驱动动画假设角色已经通过CharacterBody2D.velocity移动可以在玩家脚本中加入extends CharacterBody2D export var speed: float 240.0 onready var animated_sprite: AnimatedSprite2D $AnimatedSprite2D func _physics_process(_delta: float) - void: var input_direction : Input.get_vector( move_left, move_right, move_up, move_down ) velocity input_direction * speed move_and_slide() update_animation() func update_animation() - void: if velocity.is_zero_approx(): animated_sprite.play(idle) else: animated_sprite.play(walk)这里使用velocity而不是重新读取按键原因很实际输入表达的是玩家意图速度表达的是角色最终是否在移动。以后加入击退、过场控制或移动锁定时动画仍能跟随真实运动结果。五、避免每帧重复启动同一动画Godot 会维护当前动画状态但显式判断可以让逻辑更清楚也方便以后加入复杂动作func play_if_changed(animation_name: StringName) - void: if animated_sprite.animation ! animation_name: animated_sprite.play(animation_name) func update_animation() - void: if velocity.is_zero_approx(): play_if_changed(idle) else: play_if_changed(walk)StringName写法适合频繁使用的标识符。这里不是性能优化的硬性要求主要目的是避免无意义的重复状态设置。六、根据水平方向翻转角色如果素材只绘制了朝右的行走帧可以使用flip_hfunc update_facing() - void: if velocity.x 0.0: animated_sprite.flip_h false elif velocity.x 0.0: animated_sprite.flip_h true把它放在物理更新的动画处理阶段move_and_slide() update_facing() update_animation()当velocity.x等于零时不要改变方向这样角色停止后会保留最后一次朝向不会突然回到默认方向。七、整合后的完整脚本extends CharacterBody2D export var max_speed: float 240.0 export var acceleration: float 1200.0 export var deceleration: float 1600.0 onready var animated_sprite: AnimatedSprite2D $AnimatedSprite2D func _physics_process(delta: float) - void: var input_direction : Input.get_vector( move_left, move_right, move_up, move_down ) var target_velocity : input_direction * max_speed var rate : acceleration if not input_direction.is_zero_approx() else deceleration velocity.x move_toward(velocity.x, target_velocity.x, rate * delta) velocity.y move_toward(velocity.y, target_velocity.y, rate * delta) move_and_slide() update_facing() update_animation() func update_facing() - void: if velocity.x 0.0: animated_sprite.flip_h false elif velocity.x 0.0: animated_sprite.flip_h true func update_animation() - void: var target_animation: StringName ( idle if velocity.is_zero_approx() else walk ) if animated_sprite.animation ! target_animation: animated_sprite.play(target_animation)如果使用平滑减速松开按键后速度不会立刻归零因此walk会持续到角色真正停下。这通常比根据按键瞬间切回待机更自然。八、处理一次性动画攻击、受伤或打哈欠通常不应循环。可以监听animation_finished播放完成后恢复待机var action_locked : false func _ready() - void: animated_sprite.animation_finished.connect(_on_animation_finished) func play_one_shot(animation_name: StringName) - void: action_locked true animated_sprite.play(animation_name) func _on_animation_finished() - void: if action_locked: action_locked false update_animation()同时在update_animation()开头增加保护func update_animation() - void: if action_locked: return var target_animation: StringName ( idle if velocity.is_zero_approx() else walk ) play_if_changed(target_animation)循环动画不会发出animation_finished。因此idle和walk负责持续状态一次性动画负责在完成时释放锁定。九、常见问题动画一直停在第一帧检查动画是否添加了多帧、FPS 是否大于零以及场景运行时是否真的调用了play()。walk 动画不断从头开始不要无条件调用带重置效果的逻辑。只在目标动画与当前动画不同的时候切换。左右方向快速抖动只在velocity.x明确大于或小于零时修改flip_h速度为零时保留原方向。一次性动画刚播放就被 idle 覆盖普通移动动画每个物理帧都会更新因此需要在一次性动作期间设置锁定并在animation_finished后释放。角色松开按键后仍播放 walk如果脚本使用平滑减速这是预期行为。动画依据真实速度切换角色尚未完全停止时继续走动更符合画面。十、知识主线与参考本文的SpriteFrames组织方式、循环与一次性动画区别、animation_finished信号以及角色物理移动逻辑来自《大鹏 Godot 4 游戏开发入门》第 5 章中“动画精灵”“控制 Sprite2D 动画”和“编写物理角色移动代码”三节。示例只在这些已锁定知识上做组合不把平台同题文章当作技术依据。十一、验收清单进入场景后自动播放idle。任意方向移动时切换为walk。松开输入并完全停止后恢复idle。左右移动时角色朝向正确。仅上下移动时保留最近一次水平朝向。一次性动画不会被移动动画立即覆盖。一次性动画结束后能回到正确的待机或移动状态。总结至此角色的物理移动和视觉动画已经形成了一个清晰闭环CharacterBody2D决定真实速度AnimatedSprite2D根据速度选择画面。以后增加攻击、跳跃或受伤行为时也可以沿用“持续动画循环、一次性动画完成后回退”的原则继续扩展。