OK,OK,大家好,欢迎大家来到大鹏 AI 教育,我是张大鹏。
很多同学第一次做角色移动,会直接修改 position。角色确实动了,但遇到墙会穿过去,斜向移动还可能更快。问题不是少写了一句代码,而是从一开始就绕开了 Godot 的输入和物理系统。
这篇文章只解决一个完整问题:让 2D 角色接受键盘输入,保持统一速度,并能沿障碍物自然滑动。

先看完整思路
角色移动可以拆成四步:
- 🎮 Input Map 把具体按键抽象成动作
- 🧭
Input.get_vector()把四个动作合成为方向向量 - 🚀 把方向乘以速度,写入
velocity - 🧱
move_and_slide()完成移动、碰撞与滑动
这四步分别解决输入配置、方向计算、速度控制和物理碰撞。职责分开后,键盘改成手柄时,角色脚本几乎不用动。
第一步:不要在脚本里写死按键
打开"项目 > 项目设置 > 输入映射",创建下面四个动作:
- ⬅️
move_left - ➡️
move_right - ⬆️
move_up - ⬇️
move_down
然后分别绑定方向键或 W、A、S、D。

为什么要多做这一层?因为脚本关心的是"向左移动"这个意图,不应该关心它来自 A 键、左方向键还是手柄摇杆。
第二步:选择 CharacterBody2D
创建角色根节点时选择 CharacterBody2D。

它适合由脚本直接控制的角色。常见的三个物理节点可以这样理解:
- 🧍
CharacterBody2D:玩家、敌人等主动移动的角色 - 🧱
StaticBody2D:墙、地面等不主动移动的障碍 - 📦
RigidBody2D:箱子、球等主要由物理引擎推动的物体
角色下还需要可视节点和 CollisionShape2D。碰撞形状尽量贴合角色身体,不要把透明留白也包进去,否则玩家会感觉"还没碰到墙就被挡住了"。
第三步:用一个向量处理四方向输入
把脚本挂到 CharacterBody2D:
gdscript
extends CharacterBody2D
@export var speed: float = 200.0
func _physics_process(_delta: float) -> void:
var direction := Input.get_vector(
"move_left",
"move_right",
"move_up",
"move_down"
)
velocity = direction * speed
move_and_slide()

Input.get_vector() 会返回长度不超过 1 的向量。只按右键时大约是 (1, 0),同时按右键和下键时会得到归一化后的斜向量。
这正好解决了常见的"斜向移动更快"。如果分别给 x、y 都赋值为 speed,斜向速度会变成 speed × √2。使用 get_vector() 后,方向长度受到限制,再乘速度即可保持各方向一致。
第四步:让角色碰墙后继续滑动
场景里放一个 StaticBody2D,为它添加 CollisionShape2D。它就是角色要碰撞的墙。

关键不是自己计算"撞墙后退多少",而是调用:
gdscript
move_and_slide()
它会读取当前 velocity,移动角色,处理碰撞,并保留沿碰撞表面的有效分量。所以角色斜着撞墙时,不会僵在原地,而会顺着墙继续滑动。
注意,Godot 4 的 CharacterBody2D.move_and_slide() 不需要把 velocity 当参数传入。velocity 是节点属性,方法会直接使用并在碰撞后更新它。
停止移动时要不要减速
上面的写法松开按键后立即停止,适合响应直接的俯视角角色。如果想做带惯性的移动,可以把赋值改为逐渐逼近:
gdscript
@export var acceleration: float = 1200.0
func _physics_process(delta: float) -> void:
var direction := Input.get_vector(
"move_left",
"move_right",
"move_up",
"move_down"
)
var target_velocity := direction * speed
velocity = velocity.move_toward(target_velocity, acceleration * delta)
move_and_slide()
这里的 delta 很重要。它让加速度按时间计算,不会因为电脑帧率不同而明显改变手感。
最容易踩的四个坑
-
❌ 在
_process()里处理物理移动✅ 放到
_physics_process(),让移动与固定物理帧同步。 -
❌ 直接修改
position✅ 给
velocity赋值并调用move_and_slide()。 -
❌ Input Map 名称和代码不一致
✅ 动作名逐字核对,大小写也要一致。
-
❌ 墙只有图片,没有碰撞形状
✅
StaticBody2D和角色都必须具有有效的CollisionShape2D。
验收清单
- ✅ W、A、S、D 和方向键都能移动
- ✅ 水平、垂直、斜向速度一致
- ✅ 松开按键后行为符合设计
- ✅ 角色不能穿过静态障碍
- ✅ 斜着撞墙时能沿墙滑动
- ✅ 修改 Input Map 后不需要改角色移动代码
小结
一套可靠的 Godot 4 角色移动,不是"坐标加减法",而是一条清楚的协作链:Input Map 表达意图,Input.get_vector() 生成方向,velocity 表达速度,move_and_slide() 负责物理结果。
你现在更想把这套移动扩展成八方向动画,还是加入加速、减速和冲刺?欢迎在评论区告诉我,我会优先拆解大家最需要的部分。
参考资料
- 《大鹏 Godot 4 游戏开发入门》第 4 章 4.4 节"输入"
- 《大鹏 Godot 4 游戏开发入门》第 5 章 5.2.1 至 5.2.4 节
- Godot 官方文档:CharacterBody2D
- Godot 官方文档:Input