用户角色精灵图制作角色的完整流程
在 2D RPG 游戏里,角色通常不是用一张单独图片完成的,而是用一张“角色精灵图”来做。
所谓角色精灵图,通常是一张包含多个动作帧的大图。比如角色向下走有 4 帧,向左走有 4 帧,向右走有 4 帧,向上走有 4 帧。Godot 会根据这些帧不断切换图片,看起来角色就动起来了。
这篇文章主要介绍:拿到一张角色精灵图之后,如何在 Godot 中把它做成一个可以正常移动、播放动画、和地图产生遮挡关系的角色。
一、先理解角色精灵图是什么
角色精灵图一般长这样:
第 1 行:向下走的动画帧
第 2 行:向左走的动画帧
第 3 行:向右走的动画帧
第 4 行:向上走的动画帧
或者有些资源是这样的:
第 1 行:向下
第 2 行:向上
第 3 行:向左
第 4 行:向右
不同素材的排列顺序不一定一样,所以第一步不是急着导入,而是先确认:
每一帧多大?
一共有几行?
一共有几列?
每一行分别代表什么方向?
有没有待机动画?
有没有攻击、受伤、死亡动画?
背景是否透明?
如果你的角色资源是 32x32 的,那么意思是:
每一个角色动作帧占 32 像素宽、32 像素高
如果一张图宽 128,高 128,并且每帧是 32x32,那么它就是:
4 列 x 4 行
也就是一共 16 帧。
二、角色精灵图导入 Godot 前要注意什么
在导入之前,最好先检查这几个点。
1. 背景最好是透明的
角色精灵图一般应该是 PNG 格式,并且背景透明。
如果是白底、黑底、灰底,导入 Godot 后会出现一个方块背景,看起来很难受。
推荐格式:
PNG
透明背景
每一帧严格对齐网格
不推荐:
JPG
带白底
帧之间间隔不统一
角色位置忽上忽下
2. 每一帧尺寸要统一
比如角色每帧是 32x32,那么每一帧都应该严格占 32x32。
不要出现这种情况:
第一帧 32x32
第二帧 33x32
第三帧 31x32
否则动画播放时,角色会抖动、错位。
3. 角色脚底位置最好一致
这是初学者很容易忽略的问题。
角色动画的重点不是头部对齐,而是脚底对齐。
因为角色站在地图上的位置,是以“脚底”为视觉参考的。如果每一帧脚底位置不一样,角色走路时会像在上下跳。
正确情况:
每一帧角色脚底都在同一条水平线上
错误情况:
有的帧脚底高一点
有的帧脚底低一点
三、推荐的角色场景结构
在 Godot 中,玩家角色一般不要直接用 Sprite2D 作为根节点。
更推荐使用:
Player
├── AnimatedSprite2D
├── CollisionShape2D
├── Camera2D
└── RemoteTransform2D / Marker2D / 其他辅助节点
对于 2D RPG,根节点通常使用:
CharacterBody2D
完整结构可以是:
Player / CharacterBody2D
├── AnimatedSprite2D
├── CollisionShape2D
├── Camera2D
└── InteractionArea / Area2D
└── CollisionShape2D
每个节点的作用如下。
四、CharacterBody2D 是什么
CharacterBody2D 是 Godot 专门用来做“可移动角色”的 2D 节点。
比如:
玩家
NPC
怪物
可移动单位
都可以用它。
它的核心作用是:
负责移动
负责碰撞
负责速度
负责和墙、树、石头等障碍物发生阻挡关系
前端类比的话,可以把它理解成:
角色的逻辑容器
就像你在 Vue 或 React 中写一个 Player 组件:
<Player>
<角色图片 />
<碰撞区域 />
<摄像机 />
</Player>
CharacterBody2D 自己不负责显示图片,它只是角色的主体节点。
真正显示角色的是它下面的 AnimatedSprite2D。
五、AnimatedSprite2D 是什么
AnimatedSprite2D 是负责播放角色动画的节点。
它可以播放:
向下走
向左走
向右走
向上走
待机
攻击
受伤
死亡
它需要配合一个资源使用:
SpriteFrames
SpriteFrames 就是 Godot 里专门管理动画帧的资源。
你可以把它理解成:
角色动画配置表
里面会定义:
动画名称
每个动画包含哪些帧
每秒播放多少帧
是否循环播放
例如:
idle_down
walk_down
walk_left
walk_right
walk_up
六、CollisionShape2D 是什么
CollisionShape2D 是角色的碰撞形状。
它决定角色在地图里“真实占用的碰撞范围”。
注意,它不一定要和角色图片一样大。
对于 RPG 角色,碰撞区域通常只需要覆盖脚底附近。
比如一个 32x32 的角色,不建议碰撞框设置为完整 32x32。
更推荐:
宽度:12 到 18
高度:8 到 14
位置:角色脚底附近
因为角色的头部可以被树冠、屋顶遮住,但脚底才是真正站在地上的位置。
推荐效果:
角色图片高度:32
碰撞框只放在下半部分
示意:
角色图片:
┌────────────┐
│ 头部 │
│ 身体 │
│ 腿部 │
│ 脚底 │
└────────────┘
碰撞区域:
┌────┐
│脚底│
└────┘
不要这样:
碰撞框覆盖整个角色
否则角色会在靠近树、墙、桌子时显得距离很远,好像空气墙。
七、Camera2D 是什么
Camera2D 是摄像机。
如果它放在 Player 下面,就可以让镜头跟随角色移动。
常见设置:
Enabled:开启
Position Smoothing:开启
Position Smoothing Speed:5 到 10
这样镜头不会死死贴着角色,而是有一点平滑跟随。
如果是 RPG 游戏,Camera2D 通常放在玩家角色下面:
Player
└── Camera2D
这样角色走到哪里,镜头就跟到哪里。
八、创建角色场景的完整流程
下面是完整流程。
第一步:创建 Player 场景
在 Godot 中新建一个场景。
根节点选择:
CharacterBody2D
命名为:
Player
然后保存为:
res://scenes/player/player.tscn
推荐目录:
res://
├── assets/
│ └── characters/
│ └── player.png
├── scenes/
│ └── player/
│ └── player.tscn
└── scripts/
└── player/
└── player.gd
第二步:添加 AnimatedSprite2D
在 Player 下添加:
AnimatedSprite2D
结构变成:
Player / CharacterBody2D
└── AnimatedSprite2D
选中 AnimatedSprite2D,在 Inspector 中找到:
Sprite Frames
新建一个:
SpriteFrames
点击进去,开始配置动画。
第三步:创建动画名称
建议创建这些动画:
idle_down
idle_left
idle_right
idle_up
walk_down
walk_left
walk_right
walk_up
为什么不要只叫 walk?
因为 RPG 角色是四方向移动,每个方向的动画帧不同。
所以最好拆开。
如果以后加攻击,也可以继续加:
attack_down
attack_left
attack_right
attack_up
第四步:从精灵图中切帧
在 SpriteFrames 面板里,点击添加帧。
选择角色精灵图。
Godot 会让你设置切割方式。
如果你的角色图是:
4 列
4 行
就设置:
Horizontal:4
Vertical:4
如果你的角色图是:
3 列
4 行
就设置:
Horizontal:3
Vertical:4
如果每帧是 32x32,也要确认切出来的每一格正好是 32x32。
然后把对应帧放进对应动画里。
例如,假设资源排列是:
第 1 行:向下走
第 2 行:向左走
第 3 行:向右走
第 4 行:向上走
那么可以这样配置:
walk_down:第 1 行的所有帧
walk_left:第 2 行的所有帧
walk_right:第 3 行的所有帧
walk_up:第 4 行的所有帧
待机动画可以用每一行动画的中间帧:
idle_down:向下行的中间帧
idle_left:向左行的中间帧
idle_right:向右行的中间帧
idle_up:向上行的中间帧
如果一行动画是 4 帧,可以选第 1 帧或第 2 帧作为待机帧。
第五步:设置动画播放速度
角色走路动画不应该太快。
常见 FPS:
4 FPS
6 FPS
8 FPS
如果是像素 RPG,我建议先设置:
6 FPS
也就是每秒播放 6 帧。
如果太快,角色会像抽风。
如果太慢,角色会像拖着脚走。
第六步:添加 CollisionShape2D
在 Player 下添加:
CollisionShape2D
结构变成:
Player / CharacterBody2D
├── AnimatedSprite2D
└── CollisionShape2D
选中 CollisionShape2D,在 Shape 中创建:
RectangleShape2D
然后调整大小。
对于 32x32 角色,可以先用这个范围:
Size:14 x 10
Position:0, 8
大概让它覆盖角色脚底区域。
也可以用:
CapsuleShape2D
如果你希望碰撞更圆滑一点。
RPG 角色通常用矩形就够了。
第七步:调整 AnimatedSprite2D 的位置
这是很关键的一步。
角色图片默认是以中心点为原点显示的。
但是 RPG 游戏里,我们通常希望角色的“脚底”作为排序和站位参考。
所以你需要调整 AnimatedSprite2D 的位置,让角色脚底接近 Player 节点的原点。
比如 32x32 角色,可以设置:
AnimatedSprite2D Position:0, -16
或者根据实际图片调整。
目标是:
Player 节点的原点在角色脚底附近
这样以后做 Y Sort 的时候,角色和树、石头、房子遮挡关系才会自然。
九、为什么角色原点最好放在脚底
这是 2D RPG 里非常重要的概念。
Godot 做上下遮挡时,经常会用 Y 坐标判断谁在前面。
简单说:
Y 坐标越大,越靠前显示
Y 坐标越小,越靠后显示
比如:
角色在树的下面,角色应该盖住树干
角色在树的上面,树冠/树干应该盖住角色
Godot 判断这个关系时,看的不是角色图片整体,而是节点的 Y 坐标。
所以如果角色节点的原点在身体中心,排序就容易不准确。
更合理的是:
角色节点原点 = 角色脚底位置
因为脚底才代表角色实际站在地图上的位置。
这也是为什么很多 RPG 角色会这样设计:
Player / CharacterBody2D
├── AnimatedSprite2D 向上偏移
└── CollisionShape2D 放在脚底
十、添加角色移动脚本
给 Player 挂载脚本:
res://scripts/player/player.gd
基础脚本如下:
extends CharacterBody2D
@export var move_speed: float = 100.0
@onready var animated_sprite: AnimatedSprite2D = $AnimatedSprite2D
var last_direction: Vector2 = Vector2.DOWN
func _physics_process(delta: float) -> void:
var input_vector := Vector2.ZERO
input_vector.x = Input.get_action_strength("move_right") - Input.get_action_strength("move_left")
input_vector.y = Input.get_action_strength("move_down") - Input.get_action_strength("move_up")
input_vector = input_vector.normalized()
velocity = input_vector * move_speed
move_and_slide()
update_animation(input_vector)
func update_animation(input_vector: Vector2) -> void:
if input_vector != Vector2.ZERO:
last_direction = input_vector
if abs(input_vector.x) > abs(input_vector.y):
if input_vector.x > 0:
animated_sprite.play("walk_right")
else:
animated_sprite.play("walk_left")
else:
if input_vector.y > 0:
animated_sprite.play("walk_down")
else:
animated_sprite.play("walk_up")
else:
if abs(last_direction.x) > abs(last_direction.y):
if last_direction.x > 0:
animated_sprite.play("idle_right")
else:
animated_sprite.play("idle_left")
else:
if last_direction.y > 0:
animated_sprite.play("idle_down")
else:
animated_sprite.play("idle_up")
这段代码做了几件事:
读取输入方向
根据方向设置 velocity
调用 move_and_slide() 让角色移动并处理碰撞
根据方向播放对应动画
没有移动时播放对应方向的待机动画
十一、配置输入按键
上面的脚本里用到了这些输入动作:
move_up
move_down
move_left
move_right
你需要在 Godot 中配置。
路径:
Project > Project Settings > Input Map
添加:
move_up
move_down
move_left
move_right
然后绑定按键。
推荐:
move_up:W、↑
move_down:S、↓
move_left:A、←
move_right:D、→
这样键盘 WASD 和方向键都能控制角色。
十二、把 Player 放进地图场景
如果你已经有一个地图场景,比如:
World.tscn
结构可能是:
World / Node2D
├── GroundLayer / TileMapLayer
├── ObjectLayer / TileMapLayer
└── Player / CharacterBody2D
你可以把 player.tscn 拖进 World 场景里。
推荐结构:
World / Node2D
├── GroundLayer
├── DecorationLayer
├── ObjectLayer
├── Player
└── UI
如果你要做遮挡排序,可以再整理成:
World / Node2D
├── GroundLayer
├── YSortObjects / Node2D
│ ├── ObjectLayer
│ ├── Trees
│ ├── Stones
│ └── Player
└── UI
在 Godot 4 中,可以给父节点开启:
Y Sort Enabled
或者对相关节点设置:
Y Sort Enabled:开启
这样同一个 Y Sort 分组下的节点会根据 Y 坐标自动排序。
十三、角色和地图遮挡关系怎么做
这是你之前经常遇到的问题:角色为什么会盖住树?为什么树不能挡住角色?为什么角色走到树后面没有被遮住?
核心原因通常是:
角色、树、石头、房子不在同一个 Y Sort 排序体系里
或者:
节点原点不在正确位置
对于 RPG 来说,建议这样做:
地面层:不参与 Y Sort
草地装饰:看情况
树、石头、房子、角色:参与 Y Sort
UI:不参与 Y Sort
结构示例:
World
├── GroundLayer
├── YSortLayer
│ ├── Trees
│ ├── Stones
│ ├── Houses
│ └── Player
└── CanvasLayer
└── UI
注意:
GroundLayer 不需要参与 Y Sort
Player 需要参与 Y Sort
树、石头、房子这些会挡住角色的对象需要参与 Y Sort
并且这些对象的原点最好都在“底部中心”。
比如树:
树的原点在树干底部
角色:
角色的原点在脚底
石头:
石头的原点在底部中心
这样排序才自然。
十四、角色移动时常见问题
1. 角色不动
常见原因:
没有配置 Input Map
脚本没有挂到 Player 上
CharacterBody2D 没有调用 move_and_slide()
move_speed 是 0
输入动作名称和代码不一致
比如代码里写的是:
move_right
但 Input Map 里写成了:
right
那就不会生效。
2. 角色动画不播放
常见原因:
AnimatedSprite2D 没有 SpriteFrames
动画名称写错
脚本里的节点路径不对
动画没有帧
比如代码里写:
animated_sprite.play("walk_down")
但是 SpriteFrames 里面动画叫:
down_walk
那就不会播放。
名称必须完全一致。
3. 角色移动方向和动画方向不一致
常见原因:
精灵图方向行数搞反了
walk_up 配成了 walk_down 的帧
walk_left 和 walk_right 配反了
解决方式:
重新检查精灵图每一行代表的方向
不要只看资源说明,最好在 Godot 里实际播放一遍。
4. 角色走路时抖动
常见原因:
每一帧角色没有对齐
角色脚底位置不一致
AnimatedSprite2D 的 offset 设置不合理
帧切割错了
解决方式:
确认每帧尺寸一致
确认切割网格正确
确认角色脚底对齐
5. 角色和树的遮挡关系不对
常见原因:
没有开启 Y Sort
角色和树不在同一个 Y Sort 父节点下
角色原点不在脚底
树的原点不在树干底部
TileMapLayer 的 Z Index 设置不合理
解决方式:
把 Player 和会遮挡角色的对象放到同一个 YSortLayer
开启 Y Sort Enabled
调整 Player 和对象的原点
必要时调整 Z Index
6. 角色碰撞范围很奇怪
常见原因:
CollisionShape2D 太大
碰撞框覆盖了整个身体
碰撞框位置没有放在脚底
解决方式:
缩小碰撞框
把碰撞框移动到脚底附近
十五、推荐的最终 Player 节点结构
一个比较正式的玩家角色结构可以是:
Player / CharacterBody2D
├── AnimatedSprite2D
├── CollisionShape2D
├── Camera2D
├── InteractionArea / Area2D
│ └── CollisionShape2D
└── DebugLabel / Label
其中:
AnimatedSprite2D:显示角色动画
CollisionShape2D:角色物理碰撞
Camera2D:镜头跟随
InteractionArea:检测面前的 NPC、宝箱、门、采集物
DebugLabel:调试用,可以以后删掉
更正式一点,可以加一个方向检测点:
Player / CharacterBody2D
├── AnimatedSprite2D
├── CollisionShape2D
├── Camera2D
├── FacingMarker / Marker2D
└── InteractionArea / Area2D
└── CollisionShape2D
FacingMarker 可以表示玩家当前面对的位置。
比如玩家朝下时,它在角色下面;玩家朝左时,它在角色左边。
之后做交互时很好用:
按 E 和面前的 NPC 对话
按 E 打开面前的宝箱
按 E 采集面前的植物
十六、角色交互区域怎么做
如果你希望玩家按 E 和物体互动,可以添加:
InteractionArea / Area2D
它下面也需要:
CollisionShape2D
结构:
Player
├── AnimatedSprite2D
├── CollisionShape2D
└── InteractionArea
└── CollisionShape2D
这个区域不是用来阻挡角色的,而是用来检测附近有没有可交互对象。
比如:
NPC
宝箱
门
采集物
路牌
当玩家朝下时,交互区域放在角色下方。
当玩家朝上时,交互区域放在角色上方。
当玩家朝左时,交互区域放在角色左边。
当玩家朝右时,交互区域放在角色右边。
这样玩家就可以和“面前”的东西互动。
十七、角色精灵图资源的推荐命名
资源不要乱放,建议一开始就整理好目录。
推荐:
res://assets/characters/player/player_walk.png
res://assets/characters/player/player_idle.png
res://assets/characters/player/player_attack.png
如果只有一张总图,可以:
res://assets/characters/player/player_spritesheet.png
场景:
res://scenes/player/player.tscn
脚本:
res://scripts/player/player.gd
动画资源如果单独保存:
res://resources/animations/player_sprite_frames.tres
这样后期项目变大也不会乱。
十八、开发顺序建议
不要一开始就做复杂战斗、背包、换装。
角色制作建议按这个顺序来:
第一步:能显示角色
第二步:能播放 idle 动画
第三步:能用键盘移动
第四步:移动时播放 walk 动画
第五步:停止时保持最后方向
第六步:加入碰撞
第七步:加入摄像机跟随
第八步:处理 Y Sort 遮挡
第九步:加入交互区域
第十步:加入攻击、采集、对话等动作
这样最稳。
不要一口气全做,否则出问题的时候很难排查。
十九、最小可用版本
如果你现在只是想先跑起来,最小版本只需要:
Player / CharacterBody2D
├── AnimatedSprite2D
└── CollisionShape2D
脚本只需要:
extends CharacterBody2D
@export var move_speed: float = 100.0
@onready var animated_sprite: AnimatedSprite2D = $AnimatedSprite2D
func _physics_process(delta: float) -> void:
var input_vector := Input.get_vector(
"move_left",
"move_right",
"move_up",
"move_down"
)
velocity = input_vector * move_speed
move_and_slide()
if input_vector != Vector2.ZERO:
animated_sprite.play("walk_down")
else:
animated_sprite.play("idle_down")
这个版本虽然还没有四方向动画,但可以先确认:
角色能不能显示
角色能不能移动
碰撞有没有生效
动画能不能播放
确认没问题后,再升级到四方向动画。
二十、最终理解
通过角色精灵图制作角色,本质上是在做三件事:
第一,把一张大图切成一帧一帧的小图
第二,把这些帧组合成不同方向的动画
第三,用脚本根据玩家输入播放对应动画并移动角色
在 Godot 里,对应关系是:
角色主体:CharacterBody2D
角色显示:AnimatedSprite2D
动画资源:SpriteFrames
角色碰撞:CollisionShape2D
移动逻辑:player.gd
镜头跟随:Camera2D
遮挡排序:Y Sort
真正需要重点记住的是:
CharacterBody2D 负责移动和碰撞
AnimatedSprite2D 负责显示和动画
SpriteFrames 负责管理动画帧
CollisionShape2D 负责角色碰撞范围
角色原点最好放在脚底
会互相遮挡的对象要放在同一个 Y Sort 体系里
只要这套关系理解了,之后无论你换什么角色精灵图,本质流程都是一样的。
如果您觉得这篇文章有帮助,请点个赞吧~
评论
请登录后发表评论
去登录