从零开始的 Godot 之旅 --- EP6:更优雅地实现角色场景
在上一节里,我们通过直接修改 position
来让角色上下左右移动,虽然简单直观,但随着需求复杂(加入碰撞、斜坡、速度控制、暂停等),直接操作坐标就会显得"原始"。
这一节我们换一种官方推荐的方式:使用更专业的节点 CharacterBody2D
来驱动角色运动,并将角色抽取成一个可复用的独立场景。这样后面不论是玩家还是敌人,都可以共享一套更规范的结构。
一、为什么要换成 CharacterBody2D?
简单总结几点:
- 内置物理与碰撞:自动与 TileMap、CollisionShape2D 等协作,不用手动处理"卡墙"问题。
velocity
语义明确:后续加加速度、阻力、受击击退等更自然。- 自带辅助方法:
move_and_slide()
、is_on_floor()
等让常见逻辑极简。 - 更符合后续扩展:状态机、动画、技能系统都围绕"速度/方向"构建,比直接改
position
更统一。
当然,如果只是一个临时的演示,也可以继续用 Node2D + 直接改坐标;但只要项目要继续发展,尽早切换到 CharacterBody2D 是值得的。
二、创建节点与基础展示
在使用新节点类型前,可以先把之前的旧角色节点隐藏,避免视图里混乱:

然后在 Node2D
(主场景根)下新建节点,选择 CharacterBody2D
:
在 Godot 4 中,
CharacterBody2D
是核心的 2D 角色物理节点,继承自PhysicsBody2D
,专为"玩家""敌人"这种可控或自动移动的对象设计。
为它添加一个 Sprite2D
子节点用于显示贴图,依旧是把图片拖到 Texture
属性:
三、编写脚本:输入到移动
右键 CharacterBody2D
,添加脚本: 命名为:
player_new.gd
,示例代码如下:
ini
extends CharacterBody2D
# 可在检查器里调整的移动速度
@export var speed: float = 100.0
# 当前输入方向向量(单位化前的原始输入)
var move_direction: Vector2 = Vector2.ZERO
func _physics_process(delta: float) -> void:
# 分别获取水平方向与垂直方向输入(已自动处理互斥与摇杆)
var x_axis: float = Input.get_axis("left", "right")
var y_axis: float = Input.get_axis("up", "down")
move_direction = Vector2(x_axis, y_axis)
velocity = move_direction * speed
move_and_slide() # 内置处理碰撞与滑动
重点说明
- 使用
_physics_process
而不是_process
:物理相关的移动最好放在固定步长的物理帧回调里,避免帧率波动导致移动速度不一致。 Input.get_axis(left, right)
:可读性好,自动处理同时按下的冲突,并兼容手柄摇杆。垂直方向同理。velocity
:CharacterBody2D
内置的速度向量,移动核心都围绕它展开。@export
:将speed
暴露到检查器,后续调数值无需改脚本。
检查器中即可看到导出的属性:
运行效果:

可以看到我们不再直接操作 position
,而是通过速度与物理步驱动,这在继续扩展(碰撞、动画、受力)时更顺畅。
四、将角色封装成独立场景
为什么要单独抽成一个 player.tscn
?核心目的:复用 + 隔离 + 易维护。
- 复用:之后在多个关卡、测试场景里可以直接实例化,不必重新搭建节点结构。
- 隔离:主场景保持"干净",逻辑分层清晰,防止一个场景里塞满无关节点。
- 易维护:角色的碰撞、动画、输入、状态机未来都可以逐步在这个独立场景里扩展。
- 可替换:后面如果要做"职业切换""不同皮肤",只需换一个同结构的角色场景文件。
操作过程:
- 选中刚创建的
CharacterBody2D
分支,选择"将分支保存为新场景":
2. 命名为
player.tscn
,保存后在文件系统可见:
3. 双击打开,给根节点重命名为
Player
: 4. 回到主场景,删除旧的临时角色节点:
5. 将
player.tscn
拖入或通过右键实例化到主场景。实例化后只显示根节点本身,子节点结构被折叠,对使用者更"简洁":

此时在主场景中调整 speed
的值只影响这个实例,不会修改 player.tscn
内的默认值。以后再实例化一个新的 Player,仍然是原始速度。这个机制和面向对象里"类与实例"很类似:场景文件像"类定义",拖进主场景的是"对象实例"。

五、小扩展方向(可选)
如果你想继续优化,可以考虑:
- 输入抽象:把输入逻辑拆到一个
InputController
脚本,便于后续支持手柄/键盘切换。 - 状态机:不同状态(移动、攻击、受击、死亡)管理速度与动画。
- 动画集成:给 Player 加
AnimatedSprite2D
或AnimationTree
,根据velocity
切换方向动画。 - 碰撞:补上
CollisionShape2D
,否则后续与地图交互不完整。 - 数值调参:在检查器里再导出例如
acceleration
、friction
,实现更顺滑的移动。
这些暂时不急,我们在后续章节逐步展开。
六、本节小结
本节完成了两件重要的事情:
- 用
CharacterBody2D
替代直接改坐标,实现更规范的移动逻辑。 - 将角色抽取为独立场景,建立"可复用 + 易扩展"的结构基础。
至此,我们已经对 Godot 的基础节点、输入和简单物理有了初步认识。下一节开始,将重开一个新工程,引入一套免费的素材,正式以"俯视角角色扮演"方向继续迭代。敬请期待。