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 都共用這些重要屬性。你可以在檢視器(Inspector)中或透過程式碼調整它們。

屬性 型別 預設值 說明
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(...)
回傳 velocity
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