CharacterBody3D
2026/4/14大约 6 分钟
最后同步日期:2026-04-15 | Godot 官方原文 — CharacterBody3D
CharacterBody3D
节点继承关系
继承链:Node → Node3D → PhysicsBody3D → CharacterBody3D
继承自 PhysicsBody3D
| 类型 | 名称 | 说明 |
|---|---|---|
| 方法 | MoveAndCollide() | 移动并检测碰撞 |
| 方法 | TestMove() | 测试移动是否会碰撞(不实际移动) |
继承自 Node3D
| 类型 | 名称 | 说明 |
|---|---|---|
| 属性 | Position | 本地位置(X / Y / Z) |
| 属性 | GlobalPosition | 全局位置 |
| 属性 | Rotation | 旋转角度(欧拉角,弧度) |
| 属性 | Scale | 缩放比例 |
| 属性 | TopLevel | 是否脱离父节点的变换 |
| 方法 | LookAt() | 朝向目标点 |
| 方法 | ToGlobal() | 本地坐标转全局坐标 |
| 方法 | ToLocal() | 全局坐标转本地坐标 |
| 方法 | RotateX/Y/Z() | 绕指定轴旋转 |
继承自 Node
| 类型 | 名称 | 说明 |
|---|---|---|
| 属性 | Name | 节点名称 |
| 属性 | ProcessMode | 处理模式(始终 / 暂停时 / 仅编辑器) |
| 属性 | ProcessPriority | 处理优先级,数字越小越先执行 |
| 信号 | ready | 节点进入场景树并准备就绪 |
| 信号 | tree_entered | 节点进入场景树 |
| 信号 | tree_exited | 节点完全离开场景树 |
| 方法 | GetNode<T>() | 按路径获取子节点 |
| 方法 | AddChild() | 添加子节点 |
| 方法 | RemoveChild() | 移除子节点 |
| 方法 | QueueFree() | 帧结束后释放节点 |
| 方法 | GetParent() | 获取父节点 |
定义
专门给 3D 游戏中的玩家角色和 AI 敌人用的物理体——你自己控制它怎么动,物理引擎只帮你处理碰撞。
CharacterBody3D 和 CharacterBody2D 的原理完全一样,区别只是维度不同。在 3D 中,角色的运动方向不再局限于左右和上下,而是可以在三维空间中自由移动。
使用频率:★★★★ 维度专用常用——几乎每个有角色的 3D 游戏都会用到。
节点用途
- 第一人称/第三人称射击游戏的主角
- 3D 动作游戏的玩家角色
- AI 敌人(巡逻、追踪、逃跑)
- 3D 冒险游戏的 NPC 角色
使用场景
典型场景
- 第一人称射击:玩家在 3D 场景中行走、跳跃、与墙壁碰撞
- 第三人称动作:角色在 3D 世界中自由移动
- 3D 平台跳跃:角色在 3D 平台之间跳跃
不适用场景
- 需要真实物理模拟的物体 → 使用 RigidBody3D
- 永远不动的物体 → 使用 StaticBody3D
- 只需检测进出的区域 → 使用 Area3D
常用节点搭配
| 搭配节点 | 用途 | 必需? |
|---|---|---|
| CollisionShape3D | 定义角色的碰撞边界 | 必需 |
| MeshInstance3D | 显示角色的 3D 模型 | 推荐 |
| Camera3D | 第一人称/第三人称镜头 | 推荐 |
| NavigationAgent3D | AI 自动寻路 | 可选(AI 用) |
典型节点树:
Player (CharacterBody3D)
├── CollisionShape3D
├── MeshInstance3D
├── Camera3D
└── Neck (Node3D)
└── Head (Node3D)
└── Camera3D生效必备素材/资源
| 资源 | 类型 | 说明 |
|---|---|---|
| 碰撞形状(如 CapsuleShape3D) | Shape3D 资源 | 赋给 CollisionShape3D 子节点,定义碰撞轮廓 |
| 3D 模型 | .glb/.gltf/.obj 文件 | 角色的外观模型 |
节点属性与信号
运动控制
| 属性 | 类型 | 默认值 | 继承自 | 说明 |
|---|---|---|---|---|
MotionMode | 枚举 | Grounded | — | 运动模式:Grounded(地面模式)/ Floating(漂浮模式) |
UpDirection | Vector3 | (0, 1, 0) | — | 告诉引擎"哪个方向是上"(注意 3D 中 Y 轴正方向朝上) |
Velocity | Vector3 | (0, 0, 0) | — | 当前速度(可直接读写) |
SafeMargin | float | 0.08 | — | 碰撞安全边距 |
地面与斜坡
| 属性 | 类型 | 默认值 | 继承自 | 说明 |
|---|---|---|---|---|
FloorStopOnSlope | bool | true | — | 在斜坡上是否自动停下 |
FloorConstantSpeed | bool | false | — | 在斜坡上是否保持恒定速度 |
SlideOnCeiling | bool | true | — | 撞到天花板时是否滑动 |
WallMinSlideAngle | float | 0.26 rad | — | 最小滑动角度 |
MaxSlides | int | 4 | — | 每帧最大滑动次数 |
FloorMaxAngle | float | 0.785 rad | — | 能被识别为地面的最大倾斜角度 |
SnapLength | float | 0.1 | — | 地面吸附距离(防止角色在微小间隙处坠落) |
信号
| 信号 | 触发时机 | 继承自 | 说明 |
|---|---|---|---|
| 无自有信号 | — | — | 碰撞检测通过方法实现 |
常用方法
| 方法 | 返回值 | 说明 |
|---|---|---|
MoveAndSlide() | bool | 执行移动并处理碰撞 |
IsOnFloor() | bool | 角色是否站在地面上 |
IsOnWall() | bool | 角色是否碰到墙壁 |
IsOnCeiling() | bool | 角色是否碰到天花板 |
GetFloorAngle() | float | 获取脚下地面的倾斜角度 |
GetFloorNormal() | Vector3 | 获取脚下地面的法线方向 |
GetFloorSurfaceVelocity() | Vector3 | 获取脚下地面的表面速度 |
GetLastSlideCollision() | KinematicCollision3D | 获取最后一次碰撞信息 |
GetSlideCollisionCount() | int | 获取当前帧碰撞次数 |
MoveAndCollide(velocity) | KinematicCollision3D | 移动并获取碰撞信息(不自动滑动) |
TestMove(from, velocity) | bool | 测试移动是否会碰撞(不实际移动) |
GetPlatformVelocity() | Vector3 | 获取当前站立的移动平台的速度 |
GetPlatformOnFloor() | RigidBody3D | 获取脚下的移动平台(如果有) |
代码示例
C
using Godot;
/// <summary>
/// 3D 第一人称角色控制
/// 节点结构:Player (CharacterBody3D) + CollisionShape3D + Camera3D + Neck
/// </summary>
public partial class FirstPersonPlayer : CharacterBody3D
{
[Export] public float ExSpeed = 5.0f; // 移动速度(米/秒)
[Export] public float ExJumpVelocity = 4.5f; // 跳跃初速度
[Export] public float ExMouseSensitivity = 0.002f; // 鼠标灵敏度
private float _gravity;
private Camera3D _camera;
private Node3D _neck;
public override void _Ready()
{
_gravity = (float)ProjectSettings.GetSetting("physics/3d/default_gravity");
_neck = GetNode<Node3D>("Neck");
_camera = GetNode<Camera3D>("Neck/Camera3D");
// 锁定鼠标光标
Input.MouseMode = Input.MouseModeEnum.Captured;
}
public override void _UnhandledInput(InputEvent @event)
{
// 鼠标移动 → 旋转视角
if (@event is InputEventMouseMotion motion)
{
// 水平旋转整个角色
RotateY(-motion.Relative.X * ExMouseSensitivity);
// 垂直旋转镜头(上下看)
_neck.RotateX(-motion.Relative.Y * ExMouseSensitivity);
// 限制上下看的角度,防止翻转
_neck.Rotation = new Vector3(
Mathf.Clamp(_neck.Rotation.X, Mathf.DegToRad(-80f), Mathf.DegToRad(80f)),
0f, 0f
);
}
}
public override void _PhysicsProcess(double delta)
{
float dt = (float)delta;
// ---- 重力 ----
if (!IsOnFloor())
Velocity = new Vector3(Velocity.X, Velocity.Y - _gravity * dt, Velocity.Z);
// ---- 跳跃 ----
if (Input.IsActionJustPressed("jump") && IsOnFloor())
Velocity = new Vector3(Velocity.X, ExJumpVelocity, Velocity.Z);
// ---- 移动 ----
// 获取移动输入方向
Vector2 inputDir = Input.GetVector("move_left", "move_right", "move_forward", "move_back");
// 将 2D 输入方向转换为 3D 方向(基于角色朝向)
Vector3 direction = (Transform.Basis * new Vector3(inputDir.X, 0f, inputDir.Y)).Normalized();
if (direction != Vector3.Zero)
{
Velocity = new Vector3(direction.X * ExSpeed, Velocity.Y, direction.Z * ExSpeed);
}
else
{
// 松开按键时减速停下
Velocity = new Vector3(
Mathf.MoveToward(Velocity.X, 0f, ExSpeed),
Velocity.Y,
Mathf.MoveToward(Velocity.Z, 0f, ExSpeed)
);
}
MoveAndSlide();
}
}GDScript
# 3D 第一人称角色控制
# 节点结构:Player (CharacterBody3D) + CollisionShape3D + Camera3D + Neck
extends CharacterBody3D
@export var speed: float = 5.0 # 移动速度(米/秒)
@export var jump_velocity: float = 4.5 # 跳跃初速度
@export var mouse_sensitivity: float = 0.002 # 鼠标灵敏度
@onready var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
@onready var neck: Node3D = $Neck
@onready var camera: Camera3D = $Neck/Camera3D
func _ready() -> void:
# 锁定鼠标光标
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
func _unhandled_input(event: InputEvent) -> void:
# 鼠标移动 → 旋转视角
if event is InputEventMouseMotion:
# 水平旋转整个角色
rotate_y(-event.relative.x * mouse_sensitivity)
# 垂直旋转镜头(上下看)
neck.rotate_x(-event.relative.y * mouse_sensitivity)
# 限制上下看的角度,防止翻转
neck.rotation.x = clampf(neck.rotation.x, deg_to_rad(-80.0), deg_to_rad(80.0))
func _physics_process(delta: float) -> void:
# ---- 重力 ----
if not is_on_floor():
velocity.y -= gravity * delta
# ---- 跳跃 ----
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = jump_velocity
# ---- 移动 ----
# 获取移动输入方向
var input_dir := Input.get_vector("move_left", "move_right", "move_forward", "move_back")
# 将 2D 输入方向转换为 3D 方向(基于角色朝向)
var direction := (transform.basis * Vector3(input_dir.x, 0.0, input_dir.y)).normalized()
if direction != Vector3.ZERO:
velocity.x = direction.x * speed
velocity.z = direction.z * speed
else:
# 松开按键时减速停下
velocity.x = move_toward(velocity.x, 0.0, speed)
velocity.z = move_toward(velocity.z, 0.0, speed)
move_and_slide()2D 和 3D 的 CharacterBody 有什么区别?
核心逻辑完全一样,主要区别:
- Y 轴方向:2D 中 Y 轴向下为正(-1 = 上),3D 中 Y 轴向上为正(1 = 上)
- 速度维度:2D 用
Vector2(x, y),3D 用Vector3(x, y, z) - 移动输入:3D 需要将 2D 输入(手柄摇杆)转换到角色的 3D 朝向上
