Godot2D 序列帧动画

参考文档: https://docs.godotengine.org/zh-cn/latest/getting_started/first_2d_game/03.coding_the_player.html

对于新手入门来说, 没必要一上来就接触 3D 游戏开发, 如果游戏注重玩法直接采用 2D/2.5D 美术来构建即可.

一般 itch.io 都有不少很不错的游戏资源包, 自带人物角色动作和场景地图, 支付购买资源版权即可

如果想快速搭建自己的游戏底层架构, 采用 2D-Animation + TileMap 是很好的方向.

上面的 Godot 官方参考文档里面只说了基础的人物动画创建, 虽然直接用确实没问题, 但还是要处理上级封装成统一模块

这里采用 Godot 的脚本系统封装 Area2DCharacter.gd, 用于做统一角色抽象类

1
2
3
4
5
6
7
8
###################################
## 扩展 Area2D 人物类
###################################
extends Area2D
class_name Area2DCharacter

# 更新触发位置变动信号
signal position_changed(x:float, y:float)

对于 position_changed 的位置更新信号设计:

  • 官方的案例对于移动的处理其实十分的粗糙, 直接在角色对象采用 Input.* 相关的操作在 _process 更新位置其实是很不好的行为

  • 需要将移动功能修改成信号然后监听变动来处理, 后面提升成服务器状态同步的时候, 每个角色都会被该信号更新

  • 而且涉及在线网络游戏的时候, 所有场景人物都是由服务端驱动, 根本不需要 Input.* 相关的移动操作

这里我是 itch.io 买的 32x32|64x64 的像素图, 购买的资源包简单动作差不多4帧就能形成一个动作, 常规的动作有以下分类:

  • default: 默认动作(可有可无)

  • idle: 待机动作

  • walk: 奔跑动作

  • attack: 攻击动作

  • jump: 跳跃动作(平台类需要, 一般可有可无)

序列帧动画: https://docs.godotengine.org/zh-cn/latest/tutorials/2d/2d_sprite_animation.html

这里随便找个角色来设计成通用人物, 这里区分人物的几个基础动作:

默认帧

default

待机帧

idle1

idle2

idle3

idle4

奔跑帧

walk1

walk2

walk3

walk4

其他序列帧这里不需要就略过, 这里只是实现常见的动态序列帧; 战斗帧是很特殊的分类, 会涉及到 HitBox 相关

导入序列帧

这里需要用到的 Godot 节点除了我们之前扩展 Area2DArea2DCharacter 还需要以下节点:

  • AnimatedSprite2D: 序列帧节点

  • CollisionShape2D: 碰撞体节点

animated-sprite-1

之后创建序列动画帧 idlewalk 即可, 一般序列帧标好 {动作}0~3.png(合计4帧) 直接拖动进精灵动画帧会自动导入顺序:

animated-sprite-2

最后就是调整 2D 碰撞体盒子(HitBox), 碰撞体主要用于玩家是否撞墙/受击等检测碰撞, 2D 动画碰撞采用矩阵或者胶囊体形状检测:

animated-sprite-3

注意: 越复杂的形状碰撞体, 对于电脑性能的要求越高, 太过复杂的碰撞体形状检测起来是一笔不小性能开销

实际上还有声音(比如攻击需要在附近播放音效)/头部表情(比如人物头上标识表情)/名称(比如头像展示人物名)等处理

这是后续自行需要扩展的内容, 这里不做太多说明

代码处理

其实我不喜欢 GDScript 这种动态脚本来编写, 但是考虑到这方面受众广, 所以采用 GDScript 处理

我更加推崇采用 C# 工具链, 业务代码和第三方库可以和 Unity3D 平滑切换, 并且强类型声明防止后期维护混乱

这里的挂载 Warriorwarrior.gd 脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
extends Area2DCharacter # 声明脚本继承于自定义的 Area2DCharacter 节点

# 对外暴露的速度属性, 可以在编辑器之中调整
# 其实这部分一般是策划配表设置的, 用专属的角色表来配置角色相关属性
# 这里只是采用编辑器暴露属性调整其实不是那么符合规范
@export var speed:float = 100.0

# 关联的角色 AnimatedSprite2D 节点, 暴露在编辑器绑定节点, 也不是很合规范的处理
@export var sprite:AnimatedSprite2D

# 2D 坐标移动的速率, x = +/-1 代表左右变动, y = +/-1 代表上下变动
var velocity: Vector2 = Vector2.ZERO

# 节点初始化回调
func _ready() -> void:
# 启动时候默认绑定位置更新事件
position_changed.connect(_on_position_changed)
if sprite: # 如果有配置动画节点默认启动待机动画
sprite.play("idle")


# 节点物理更新回调, 对于移动操作建议我习惯性采用在物理更新执行
func _physics_process(delta: float) -> void:
if velocity.length() > 0: # 检测是否 x/y 轴有发生移动操控事件
velocity = velocity.normalized() * speed # 获取移动的向量归一
sprite.play("walk") # 如果发生移动播放动画的移动 walk 事件
else:
sprite.play("idle") # 如果没有发生移动就复位成待机 idle 事件

# 修改当前节点在游戏世界之中位置
position += velocity * delta

# 因为这里的人物左右动画, 没有平面的上下动画, 所以只需要按照方向反转|正转图片朝向
# X 轴翻转采用 flip_h, Y 轴翻转采用 flip_v
if velocity.x != 0:
sprite.flip_h = velocity.x < 0

# 触发位置变动事件, 获取 Axis 轴变动
func _on_position_changed(x: float, y: float) -> void:
velocity.x = x
velocity.y = y

在 Godot 的编辑器之中绑定好主要动画节点和速度:

animated-sprite-4

这里需要搭建主场景监听键盘的移动输入和放置人物角色:

animated-sprite-5

这里的 main.gd 脚本相对简单, 并且因为人物节点都是继承 Area2DCharacter 可以手动编辑器挂载查看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
extends Node2D

# 暴露在编辑器上的选中的人物节点
@export var character:Area2DCharacter

# 节点初始化回调
func _ready() -> void:
# 如果节点非 null 就直接停止当前节点物理更新功能
if not character:
set_physics_process(false)


# 节点物理更新回调
func _physics_process(_delta: float) -> void:
# 直接监听 Input.* 相关事件, 然后推送给命中的人物节点提交信号事件
var x:float = Input.get_axis("move_left","move_right")
var y:float = Input.get_axis("move_up","move_down")
character.position_changed.emit(x,y)

最后就是拖动人物到正中央并且设置展示人物即可:

animated-sprite-6

可以选中人物节点微调下整体人物移动速度, 点击选择主场景的运行就能看到最后人物效果:

animated-sprite-7