Godot开发-碰撞体与碰撞

前言

Godot 4.x(包括 C# 和 GDScript)中,CollisionObject2D 是一个抽象基类,用于表示所有能参与碰撞检测或区域交互的 2D 节点
它本身不能直接实例化,但有两个主要子类:

CollisionObject2D 的直接子类(Godot 4)

子类 说明 是否产生物理碰撞 是否触发信号 典型用途
PhysicsBody2D 可参与物理模拟的“实体” ✅ 是 ✅(如被 Area 检测到) 玩家、敌人、箱子等
Area2D 非实体的“感应区域” ❌ 否(无碰撞体积) ✅(可检测进入/离开) 伤害区、触发器、音效范围

注意:

这两个是 唯二的直接子类

PhysicsBody2D 的子类(间接子类)

虽然 PhysicsBody2D 本身是 CollisionObject2D 的子类,但它还有自己的派生类,

因此以下节点也是 CollisionObject2D 的间接子类

来自 PhysicsBody2D 的具体实现:

  1. CharacterBody2D

    用于精确控制的角色(如玩家)

    使用 MoveAndSlide() 处理碰撞

  2. RigidBody2D

    受物理引擎完全控制(重力、冲量、碰撞反弹等)

  3. StaticBody2D

    静态环境物体(地面、墙壁),不移动,但可被其他物理体碰撞

类型与关系

继承关系图

1
2
3
4
5
6
7
8
9
Node
└── CanvasItem
└── Node2D
└── CollisionObject2D (abstract)
├── PhysicsBody2D (abstract)
│ ├── CharacterBody2D
│ ├── RigidBody2D
│ └── StaticBody2D
└── Area2D

区别

能力 Area2D CharacterBody2D RigidBody2D StaticBody2D
有碰撞体积
阻挡其他物体
受物理力影响
可编程移动 ✅(位置) ✅(速度+滑动) ⚠️(不推荐直接设位置) ❌(可设但不推荐)
触发 BodyEntered ✅(作为监听方) ✅(可被 Area 监听)
自身可监听碰撞 ✅(通过 MoveAndSlide) ✅(通过信号)

注意

💡 Area2D 虽然有碰撞形状(如 CollisionShape2D),但不会阻挡任何物理体,仅用于“检测重叠”。

判断类型示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public override void _Ready()
{
// 假设 this 是某个 CollisionObject2D 子类
if (this is Area2D)
{
GD.Print("这是一个感应区域");
}
else if (this is CharacterBody2D)
{
GD.Print("这是一个角色");
}
else if (this is RigidBody2D rb)
{
rb.ApplyCentralImpulse(new Vector2(100, 0));
}
else if (this is StaticBody2D)
{
GD.Print("这是静态环境");
}
}

总结

  • 只有两个直接子类PhysicsBody2DArea2D
  • 所有能“被碰撞”或“检测碰撞”的 2D 节点都继承自 CollisionObject2D
  • 选择依据:
    • 阻挡+移动控制CharacterBody2D
    • 真实物理RigidBody2D
    • 静态环境StaticBody2D
    • 触发区域(不阻挡)Area2D

Layer 与 Mask

在 Godot 的 2D 物理系统中,每个 CollisionObject2D 都有两个关键属性:

  1. Collision Layer(碰撞层)

    表示“我属于哪些层”。

    其他对象如果其 mask 包含这些层,就能和我碰撞。

  2. Collision Mask(碰撞掩码)

    表示“我会检测哪些层上的对象”。

    只有当对方在这些层中,我才尝试与其碰撞。

Area2D信号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public override void _Ready()
{ // 连接信号
AreaEntered += OnAreaEntered;
AreaExited += OnAreaExited;
BodyEntered += OnBodyEntered;
BodyExited += OnBodyExited;
}

private void OnAreaEntered(Area2D area)
{
GD.Print("另一个 Area 进入了!", area.Name);
}

private void OnAreaExited(Area2D area)
{
GD.Print("另一个 Area 离开了!", area.Name);
}

private void OnBodyEntered(Node2D body)
{
GD.Print("一个 PhysicsBody 进入了!", body.Name);
}

private void OnBodyExited(Node2D body)
{
GD.Print("一个 PhysicsBody 离开了!", body.Name);
}

施加力

ApplyCentralImpulseRigidBody2D(2D)或 RigidBody3D(3D)类中的一个方法,用于给刚体施加一个瞬时的中心冲量(impulse),从而立即改变其线性速度,而不影响角速度(即不会产生旋转)。

1
public void ApplyCentralImpulse(Vector2 impulse)
  • 参数impulse —— 一个表示冲量大小和方向的向量(单位:kg·m/s)。
  • 作用点:刚体的质心(中心)
  • 效果:只改变线性速度不产生扭矩(因此不会引起旋转)。

仅对 RigidBody 有效
CharacterBody2DStaticBody2D不能使用此方法。只有 RigidBody2D/RigidBody3D 支持。

必须在物理过程中调用
建议在 _PhysicsProcess() 或信号回调(如碰撞检测)中调用,以确保与物理引擎同步。

冲量(Impulse) vs 力(Force)

类型 方法 特点
力(Force) AddForce() / ApplyForce() 持续作用,受物理帧和质量影响,适合持续推动(如汽车引擎)。
冲量(Impulse) ApplyCentralImpulse() / ApplyImpulse() 瞬时作用,立即改变速度,适合跳跃、爆炸、击退等瞬间动作。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 水平移动:使用 Input.GetAxis 获取平滑输入
float horizontalInput = Input.GetAxis("move_left", "move_right");

MoveAndSlide();

if (horizontalInput != 0)
{
SetCollisionMaskValue(3, false);
}
else
{
SetCollisionMaskValue(3, true);
}

int num = GetSlideCollisionCount();
for (int i = 0; i < num; i++)
{
var collision = GetSlideCollision(i);
var collider = collision.GetCollider();
if (collider is RigidBody2D rb)
{
rb.ApplyCentralImpulse(collision.GetNormal() * -200f);
}
}

常用方法

其中

1
SetCollisionMaskValue(3, false);

是物体移动时关闭对层3的检测,可以穿透。

注意

上面关闭检测为什么还能检测到碰撞到物体是因为:这里还是获取设置状态这一帧的碰撞,后续帧是不能检测的。

方法

1
public int GetSlideCollisionCount()
  • 返回值int,表示上一次 MoveAndSlide() 调用过程中发生的滑动碰撞数量。
  • 适用节点CharacterBody2D
1
GetSlideCollision(i)
  • 获取第 i 次滑动碰撞的详细信息

其他方法

属性/方法 说明
.GetPosition() 碰撞发生的世界坐标位置(Vector2
.GetNormal() 碰撞面的法线方向(指向角色外部,Vector2
.GetTravel() 本次移动中实际走过的位移(Vector2
.GetRemainder() 未完成的剩余位移(因碰撞被阻挡的部分,Vector2
.GetCollider() 碰撞到的对象(如 NodeRigidBody2DStaticBody2D 等)
.GetColliderVelocity() 被碰撞对象的速度(如果它是移动的刚体)
.GetColliderId() 碰撞体的唯一 ID(可用于识别)