Godot 4 中的 CharacterBody2D/3D — 完整指南

1. 引言

Godot 4 对角色移动引入了重大变化。旧的 KinematicBody2DKinematicBody3D 节点被重命名为 CharacterBody2DCharacterBody3D,而 move_and_slide() API 也被彻底重新设计。无论你是从 Godot 3 迁移还是从零开始,本指南都涵盖了你需要了解的一切。

CharacterBody2DCharacterBody3D 是专为通过代码控制的角色而设计的物理体。与 RigidBody 不同,它们不会自动响应力——你完全通过脚本来控制它们的移动。这使它们成为玩家角色、NPC,以及任何需要精确、确定性移动的对象的理想选择。

2. 相较于 Godot 3 的变化

以下是 Godot 3 到 Godot 4 中角色移动方面最重要的变化:

关键要点

在 Godot 3 中,move_and_slide() 返回计算后的速度。在 Godot 4 中,它返回一个 bool(是否发生了碰撞),而速度会就地更新到 velocity 属性上。

3. 基础 2D 平台游戏控制器

这是标准的横向卷轴平台游戏控制器。角色可以左右移动,并在地面上时跳跃。这也是你创建新的 CharacterBody2D 脚本时 Godot 生成的模板。

GDScript — platformer_controller.gd
extends CharacterBody2D

const SPEED = 300.0
const JUMP_VELOCITY = -400.0

func _physics_process(delta: float) -> void:
    # Apply gravity when not on floor
    if not is_on_floor():
        velocity += get_gravity() * delta

    # Jump when on floor and jump pressed
    if Input.is_action_just_pressed("ui_accept") and is_on_floor():
        velocity.y = JUMP_VELOCITY

    # Horizontal movement
    var direction := Input.get_axis("ui_left", "ui_right")
    if direction:
        velocity.x = direction * SPEED
    else:
        velocity.x = move_toward(velocity.x, 0, SPEED)

    move_and_slide()

逐行解析

提示:必需的节点结构

你的 CharacterBody2D 至少需要一个带有指定形状(如 RectangleShape2D 或 CapsuleShape2D)的 CollisionShape2D 子节点。否则 move_and_slide() 将无法检测到任何碰撞。

4. 基础 2D 俯视角控制器

对于俯视角游戏(RPG、双摇杆射击等),你在没有重力的情况下向四个方向移动。代码比平台游戏更简单。

GDScript — topdown_controller.gd
extends CharacterBody2D

const SPEED = 200.0

func _physics_process(delta: float) -> void:
    var input_direction := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
    velocity = input_direction * SPEED
    move_and_slide()

工作原理

添加加速度与摩擦力

若要获得带加速和减速的更流畅手感:

GDScript — topdown_smooth.gd
extends CharacterBody2D

const SPEED = 200.0
const ACCELERATION = 1200.0
const FRICTION = 1000.0

func _physics_process(delta: float) -> void:
    var input_direction := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")

    if input_direction != Vector2.ZERO:
        velocity = velocity.move_toward(input_direction * SPEED, ACCELERATION * delta)
    else:
        velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)

    move_and_slide()

5. 3D 第三人称控制器

3D 版本遵循相同的模式。主要区别在于使用 Vector3,并通过 transform.basis 将 2D 输入转换为 3D 世界空间中的移动。

GDScript — character_3d.gd
extends CharacterBody3D

const SPEED = 5.0
const JUMP_VELOCITY = 4.5

func _physics_process(delta: float) -> void:
    # Apply gravity
    if not is_on_floor():
        velocity += get_gravity() * delta

    # Jump
    if Input.is_action_just_pressed("ui_accept") and is_on_floor():
        velocity.y = JUMP_VELOCITY

    # Get input and convert to 3D direction
    var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
    var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()

    if direction:
        velocity.x = direction.x * SPEED
        velocity.z = direction.z * SPEED
    else:
        velocity.x = move_toward(velocity.x, 0, SPEED)
        velocity.z = move_toward(velocity.z, 0, SPEED)

    move_and_slide()

与 2D 的主要区别

警告:get_gravity() 需要 Godot 4.4+

get_gravity() 方法在 Godot 4.4 中新增。对于更早的版本,2D 请使用 Vector2(0, ProjectSettings.get_setting("physics/2d/default_gravity")),3D 请使用 Vector3(0, -ProjectSettings.get_setting("physics/3d/default_gravity"), 0)

6. 关键属性参考

CharacterBody2DCharacterBody3D 都共享这些重要属性。你可以在检查器中或通过代码调整它们。

属性 类型 默认值 说明
velocity Vector2 / Vector3 Vector2.ZERO 角色的速度。在调用 move_and_slide() 之前设置它。调用之后会用计算得到的速度更新它。
floor_snap_length float 1.0 替代 move_and_slide_with_snap()。将角色贴附到地面的距离。设为 0 可禁用。对坡度和台阶很有用。
up_direction Vector2 / Vector3 Vector2.UP 定义哪个方向是“上”。这决定了什么算作地板、墙壁或天花板。2D 中默认为 (0, -1),3D 中默认为 (0, 1, 0)
floor_stop_on_slope bool true 为 true 时,角色静止时不会沿坡度下滑。对平台游戏至关重要。
floor_max_angle float 0.785 (45°) 可行走的最大坡度角(以弧度表示)。比此更陡的表面会被视为墙壁。使用 deg_to_rad(60) 可设为 60 度。
max_slides int 6 每次 move_and_slide() 调用的最大碰撞迭代次数。值越高越精确,但速度越慢。
wall_min_slide_angle float 0.262 (15°) 墙面滑动的最小角度。防止角色卡在几乎平行的墙壁上。
platform_on_leave PlatformOnLeave ADD_VELOCITY 离开移动平台时的行为。ADD_VELOCITY 保留动量,ADD_UPWARD_VELOCITY 只添加向上的分量,DO_NOTHING 忽略平台的速度。
slide_on_ceiling bool true 为 true 时,允许在天花板上滑动。为 false 时,撞到天花板时停止水平速度。

7. move_and_slide() 之后的碰撞检测

调用 move_and_slide() 之后,你可以检查发生的碰撞。这对于响应特定的碰撞体类型、着地时播放音效或实现蹬墙跳机制很有用。

GDScript — collision_detection.gd
func _physics_process(delta: float) -> void:
    # ... set velocity here ...
    move_and_slide()

    # Check all collisions from this frame
    for i in get_slide_collision_count():
        var collision := get_slide_collision(i)
        var collider := collision.get_collider()
        print("Collided with: ", collider.name)
        print("Normal: ", collision.get_normal())
        print("Position: ", collision.get_position())

    # Practical example: bounce off enemies
    for i in get_slide_collision_count():
        var collision := get_slide_collision(i)
        if collision.get_collider().is_in_group("enemies"):
            velocity = collision.get_normal() * 300
            break

实用的 KinematicCollision 方法

8. 迁移速查表

将 Godot 3 代码转换为 Godot 4 的快速参考:

Godot 3 Godot 4
KinematicBody2D CharacterBody2D
KinematicBody3D CharacterBody3D
velocity = move_and_slide(velocity, Vector2.UP) velocity = ...
move_and_slide()
move_and_slide_with_snap(vel, snap, up) floor_snap_length = 4.0
move_and_slide()
var vel = move_and_slide(...)
返回速度
move_and_slide()
velocity 属性就地更新
var gravity = ProjectSettings.get("physics/2d/default_gravity") get_gravity() (4.4+)
move_and_slide(..., up_direction, ...) up_direction = Vector2.UP (作为属性设置)
move_and_slide(..., stop_on_slope, ...) floor_stop_on_slope = true (作为属性设置)
Godot 的自动转换工具

在 Godot 4 中打开 Godot 3 项目时,引擎会提示转换你的项目。它会重命名节点并尝试更新脚本,但并不总能正确处理 move_and_slide() 的调用。你很可能需要手动修复移动代码。

9. 常见错误

错误 1:向 move_and_slide() 传递参数

错误
# ERROR: move_and_slide() takes no arguments in Godot 4
velocity = move_and_slide(velocity, Vector2.UP)
正确
# Set velocity, then call move_and_slide() with no arguments
velocity.x = direction * SPEED
move_and_slide()

错误 2:在 move_and_slide() 之前忘记设置 velocity

错误 —— velocity 从未被设置,因此什么都不会移动
func _physics_process(delta: float) -> void:
    move_and_slide()  # velocity is Vector2.ZERO — nothing happens

错误 3:使用 _process() 而非 _physics_process()

move_and_slide() 必须在 _physics_process() 中调用,它以固定速率运行(默认 60 Hz)。使用 _process() 会把物理与渲染帧率绑定,导致行为不一致。

错误 4:未设置 CollisionShape

没有 CollisionShape 子节点的 CharacterBody2D/CharacterBody3D 会穿过一切。如果缺少碰撞形状,Godot 会在编辑器中显示一个警告图标。

错误 5:每帧用重力覆盖 velocity.y

错误 —— 重置了 velocity.y,导致跳跃永远无效
# This overwrites any jump velocity!
velocity.y = gravity * delta  # should be +=, not =
正确 —— 累积重力
# Gravity accumulates over time
velocity += get_gravity() * delta

错误 6:在地面上时仍施加重力

始终将重力包裹在 if not is_on_floor() 中。没有这个检查,重力会在地面上持续累积。当角色从边缘走下去时,它会以极快的速度骤降,而不是自然下落。

10. 用 AI 搭建角色控制器

想让 AI 帮你搭建角色控制器吗?

Godot MCP Pro 让 AI 助手能够创建 CharacterBody 节点、配置物理属性、设置碰撞形状、编写移动脚本、启动你的游戏并测试移动——全部通过自然语言提示完成。

add_node setup_physics_body setup_collision set_physics_layers create_script simulate_key play_scene capture_frames
获取 Godot MCP Pro — $15