CharacterBody2D
2026/4/14大约 8 分钟
最后同步日期:2026-04-15 | Godot 官方原文 — CharacterBody2D
CharacterBody2D
节点继承关系
继承链:Node → CanvasItem → Node2D → PhysicsBody2D → CharacterBody2D
继承自 PhysicsBody2D
| 类型 | 名称 | 说明 |
|---|---|---|
| 方法 | MoveAndCollide() | 移动并检测碰撞 |
| 方法 | TestMove() | 测试移动是否会碰撞(不实际移动) |
继承自 Node2D
| 类型 | 名称 | 说明 |
|---|---|---|
| 属性 | Position | 本地位置(相对于父节点) |
| 属性 | GlobalPosition | 全局位置 |
| 属性 | Rotation | 旋转角度(弧度) |
| 属性 | Scale | 缩放比例 |
| 属性 | Skew | 倾斜角度(弧度) |
| 方法 | Rotate() | 旋转指定弧度 |
| 方法 | ToGlobal() | 本地坐标转全局坐标 |
| 方法 | ToLocal() | 全局坐标转本地坐标 |
继承自 CanvasItem
| 类型 | 名称 | 说明 |
|---|---|---|
| 属性 | Visible | 是否可见 |
| 属性 | Modulate | 整体颜色叠加(乘法) |
| 属性 | SelfModulate | 自身颜色叠加(不影响子节点) |
| 属性 | ZIndex | 绘制层级(Z 轴排序) |
| 信号 | visibility_changed | 可见性变化时触发 |
| 方法 | GetGlobalMousePosition() | 获取鼠标全局坐标 |
继承自 Node
| 类型 | 名称 | 说明 |
|---|---|---|
| 属性 | Name | 节点名称 |
| 属性 | ProcessMode | 处理模式(始终 / 暂停时 / 仅编辑器) |
| 属性 | ProcessPriority | 处理优先级,数字越小越先执行 |
| 信号 | ready | 节点进入场景树并准备就绪 |
| 信号 | tree_entered | 节点进入场景树 |
| 信号 | tree_exited | 节点完全离开场景树 |
| 方法 | GetNode<T>() | 按路径获取子节点 |
| 方法 | AddChild() | 添加子节点 |
| 方法 | RemoveChild() | 移除子节点 |
| 方法 | QueueFree() | 帧结束后释放节点 |
| 方法 | GetParent() | 获取父节点 |
定义
专门给 2D 游戏中的玩家角色和 AI 敌人用的物理体——你自己控制它怎么动,物理引擎只帮你处理碰撞。
想象你在真实世界里操控一个机器人——你决定它往哪走、什么时候跳,但如果有堵墙挡着,它就走不过去。CharacterBody2D 就是这个"机器人"。
它的核心思路是:你来决定速度,引擎来处理碰撞结果。你调用 move_and_slide()(滑动并移动),引擎就会自动让角色沿着墙壁和地面滑动,而不是卡死在墙里。
使用频率:★★★★ 维度专用常用——几乎每个有角色的 2D 游戏都会用到。
节点用途
- 玩家角色(横版跳跃、俯视角 RPG、格斗游戏)
- AI 敌人(跟随玩家、巡逻路线、追踪目标)
- NPC 角色(按路线行走的非玩家角色)
- 任何你想自己控制移动逻辑的角色
使用场景
典型场景
- 平台跳跃游戏:玩家左右移动、跳跃、落在平台上
- 俯视角游戏:玩家在地图上自由行走,碰到墙壁停下
- 格斗游戏:角色在场景中自由移动,与对手碰撞
不适用场景
- 需要真实物理弹跳效果的物体 → 使用 RigidBody2D
- 永远不动的地板和墙壁 → 使用 StaticBody2D
- 只需要检测物体进出的区域 → 使用 Area2D
常用节点搭配
| 搭配节点 | 用途 | 必需? |
|---|---|---|
| CollisionShape2D | 定义角色的碰撞边界 | 必需 |
| AnimatedSprite2D | 播放角色的行走/跳跃/待机动画 | 推荐 |
| Sprite2D | 显示角色的静态图片 | 可选 |
| Camera2D | 让镜头跟随角色移动 | 推荐 |
典型节点树:
Player (CharacterBody2D)
├── CollisionShape2D
├── AnimatedSprite2D
└── Camera2D生效必备素材/资源
| 资源 | 类型 | 说明 |
|---|---|---|
| 碰撞形状(如 CapsuleShape2D) | Shape2D 资源 | 赋给 CollisionShape2D 子节点,定义碰撞轮廓 |
| 角色动画帧 | SpriteFrames 资源 | 赋给 AnimatedSprite2D,提供行走/跳跃等动画 |
节点属性与信号
运动控制
| 属性 | 类型 | 默认值 | 继承自 | 说明 |
|---|---|---|---|---|
MotionMode | 枚举 | Grounded | — | 运动模式:Grounded(地面模式,有重力和上下概念)/ Floating(漂浮模式,没有重力方向) |
UpDirection | Vector2 | (0, -1) | — | 告诉引擎"哪个方向是上",用于判断地面 |
Velocity | Vector2 | (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 | — | 能被识别为地面的最大倾斜角度 |
信号
| 信号 | 触发时机 | 继承自 | 说明 |
|---|---|---|---|
| 无自有信号 | — | — | CharacterBody2D 自身不发出信号,碰撞检测通过方法实现 |
常用方法
| 方法 | 返回值 | 说明 |
|---|---|---|
MoveAndSlide() | bool | 执行移动并处理碰撞,返回是否有碰撞发生 |
IsOnFloor() | bool | 角色是否站在地面上 |
IsOnWall() | bool | 角色是否碰到墙壁 |
IsOnCeiling() | bool | 角色是否碰到天花板 |
GetFloorAngle() | float | 获取脚下地面的倾斜角度 |
GetFloorNormal() | Vector2 | 获取脚下地面的法线方向 |
GetFloorSurfaceVelocity() | Vector2 | 获取脚下地面的表面速度(如传送带) |
GetLastSlideCollision() | KinematicCollision | 获取最后一次滑动碰撞的信息 |
GetSlideCollisionCount() | int | 获取当前帧的碰撞次数 |
GetSlideCollision(index) | KinematicCollision | 获取指定索引的碰撞信息 |
MoveAndCollide(velocity) | KinematicCollision | 移动并获取碰撞信息(不自动滑动) |
TestMove(from, velocity) | bool | 测试从某个位置移动是否会碰撞(不实际移动) |
代码示例
C
using Godot;
/// <summary>
/// 2D 平台游戏角色控制
/// 节点结构:Player (CharacterBody2D) + AnimatedSprite2D + CollisionShape2D
/// </summary>
public partial class Player : CharacterBody2D
{
// ---- 参数设置 ----
[Export] public float ExSpeed = 300.0f; // 水平移动速度(像素/秒)
[Export] public float ExJumpVelocity = -400.0f; // 跳跃初速度(负数=向上)
[Export] public float ExCoyoteTime = 0.1f; // "土狼时间":离开平台后仍可跳跃的缓冲时间
[Export] public float ExJumpBufferTime = 0.1f; // 跳跃缓冲:提前按跳也能在落地瞬间跳起来
private float _gravity; // 重力加速度
private float _coyoteTimer; // 土狼时间计时器
private float _jumpBufferTimer; // 跳跃缓冲计时器
private AnimatedSprite2D _sprite;
public override void _Ready()
{
// 从项目设置中读取全局重力值
_gravity = (float)ProjectSettings.GetSetting("physics/2d/default_gravity");
_sprite = GetNode<AnimatedSprite2D>("AnimatedSprite2D");
}
public override void _PhysicsProcess(double delta)
{
float dt = (float)delta;
// ---- 1. 土狼时间计时 ----
if (IsOnFloor())
_coyoteTimer = ExCoyoteTime; // 在地面上时,持续刷新土狼时间
else
_coyoteTimer -= dt; // 离开地面后开始倒计时
// ---- 2. 跳跃缓冲计时 ----
if (Input.IsActionJustPressed("jump"))
_jumpBufferTimer = ExJumpBufferTime; // 按下跳跃键时,开始缓冲倒计时
else
_jumpBufferTimer -= dt; // 倒计时递减
// ---- 3. 重力 ----
// 不在地面上时,速度的 Y 分量持续增加(向下加速)
if (!IsOnFloor())
Velocity = new Vector2(Velocity.X, Velocity.Y + _gravity * dt);
// ---- 4. 跳跃 ----
// 土狼时间内 + 跳跃缓冲时间内 → 执行跳跃
if (_jumpBufferTimer > 0f && _coyoteTimer > 0f)
{
Velocity = new Vector2(Velocity.X, ExJumpVelocity);
_coyoteTimer = 0f; // 跳跃后清零,防止二段跳
_jumpBufferTimer = 0f;
}
// ---- 5. 左右移动 ----
float direction = Input.GetAxis("move_left", "move_right");
if (direction != 0f)
{
Velocity = new Vector2(direction * ExSpeed, Velocity.Y);
// 翻转角色朝向
_sprite.FlipH = direction < 0f;
}
else
{
// 松开按键时平滑停下
Velocity = new Vector2(Mathf.MoveToward(Velocity.X, 0, ExSpeed), Velocity.Y);
}
// ---- 6. 执行移动 ----
// MoveAndSlide 会自动处理与墙壁/地板的碰撞
MoveAndSlide();
// ---- 7. 播放动画 ----
if (IsOnFloor())
{
if (direction != 0f)
_sprite.Play("run");
else
_sprite.Play("idle");
}
else
{
// 在空中时根据纵向速度判断是上升还是下落
_sprite.Play(Velocity.Y < 0f ? "jump" : "fall");
}
}
}GDScript
# 2D 平台游戏角色控制
# 节点结构:Player (CharacterBody2D) + AnimatedSprite2D + CollisionShape2D
extends CharacterBody2D
# ---- 参数设置 ----
@export var speed: float = 300.0 # 水平移动速度(像素/秒)
@export var jump_velocity: float = -400.0 # 跳跃初速度(负数=向上)
@export var coyote_time: float = 0.1 # "土狼时间":离开平台后仍可跳跃的缓冲时间
@export var jump_buffer_time: float = 0.1 # 跳跃缓冲:提前按跳也能在落地瞬间跳起来
# 从项目设置中读取全局重力值
@onready var gravity: float = ProjectSettings.get_setting("physics/2d/default_gravity")
@onready var sprite: AnimatedSprite2D = $AnimatedSprite2D
var coyote_timer: float = 0.0 # 土狼时间计时器
var jump_buffer_timer: float = 0.0 # 跳跃缓冲计时器
func _physics_process(delta: float) -> void:
# ---- 1. 土狼时间计时 ----
if is_on_floor():
coyote_timer = coyote_time # 在地面上时,持续刷新土狼时间
else:
coyote_timer -= delta # 离开地面后开始倒计时
# ---- 2. 跳跃缓冲计时 ----
if Input.is_action_just_pressed("jump"):
jump_buffer_timer = jump_buffer_time # 按下跳跃键时,开始缓冲倒计时
else:
jump_buffer_timer -= delta # 倒计时递减
# ---- 3. 重力 ----
# 不在地面上时,速度的 Y 分量持续增加(向下加速)
if not is_on_floor():
velocity.y += gravity * delta
# ---- 4. 跳跃 ----
# 土狼时间内 + 跳跃缓冲时间内 → 执行跳跃
if jump_buffer_timer > 0.0 and coyote_timer > 0.0:
velocity.y = jump_velocity
coyote_timer = 0.0 # 跳跃后清零,防止二段跳
jump_buffer_timer = 0.0
# ---- 5. 左右移动 ----
var direction := Input.get_axis("move_left", "move_right")
if direction != 0.0:
velocity.x = direction * speed
# 翻转角色朝向
sprite.flip_h = direction < 0.0
else:
# 松开按键时平滑停下
velocity.x = move_toward(velocity.x, 0.0, speed)
# ---- 6. 执行移动 ----
# move_and_slide() 会自动处理与墙壁/地板的碰撞
move_and_slide()
# ---- 7. 播放动画 ----
if is_on_floor():
if direction != 0.0:
sprite.play("run")
else:
sprite.play("idle")
else:
# 在空中时根据纵向速度判断是上升还是下落
sprite.play("jump" if velocity.y < 0.0 else "fall")什么是"土狼时间"和"跳跃缓冲"?
- 土狼时间(Coyote Time):就像《威利狼与哔哔鸟》里的威利狼,跑出悬崖后还能在空中停一瞬间。给玩家离开平台后一小段缓冲时间,让操作更宽容。
- 跳跃缓冲(Jump Buffer):玩家在落地前稍微提前按了跳跃键,系统会"记住"这次按键,等角色落地的瞬间自动执行跳跃。让操作更流畅。
