为什么碰撞图层很重要

碰撞图层是 Godot 4 中所有物理交互的基础 — 包括移动、命中检测、射线投射、Area 触发器等等。理解 图层(Layer)掩码(Mask) 之间的区别至关重要,而误解它正是初学者和资深开发者共同最常遇到的痛点之一。

图层 vs 掩码 — 思维模型

Godot 中的每个物理对象都有两个位掩码属性:

关键规则

当 A 的掩码包含 B 的图层,或者 B 的掩码包含 A 的图层时,A 与 B 发生碰撞。只要有一方能「看到」另一方,碰撞就会发生。

在项目设置中为图层命名

在编写任何代码之前,先为你的图层命名。这会让检查器(Inspector)更易于使用,并在项目变大时防止混乱。

项目设置 > 图层名称 > 2D 物理(或 3D 物理):

图层编号 名称 用途
1Player玩家角色
2Enemy所有敌人实体
3Environment墙壁、地面、平台
4Projectile子弹、箭矢、法术
5Pickup物品、金币、回复包
6Trigger传感器、生成区、检查点

在代码中设置图层(GDScript)

Godot 4 提供两种方式 — 基于值的 API(推荐)和直接的位掩码赋值:

# Godot 4 API — value-based (1-indexed, recommended)
set_collision_layer_value(1, true)   # I am on layer 1
set_collision_mask_value(2, true)    # I detect layer 2

# Or use bitmask directly
collision_layer = 1   # Layer 1 only (bit 0)
collision_mask = 6    # Layers 2 and 3 (bits 1 + 2 = 6)

# Read current state
var on_layer_1: bool = get_collision_layer_value(1)
var scans_layer_2: bool = get_collision_mask_value(2)

位掩码陷阱

set_collision_layer_value() 中,图层编号是 从 1 开始 的,但底层的位掩码是 从 0 开始 的。图层 1 = bit 0 = 值 1,图层 2 = bit 1 = 值 2,图层 3 = bit 2 = 值 4。拿不准时,就使用基于值的 API 以避免错误。

常见图层方案

平台跳跃 / 横版卷轴

对象 Layer Mask 说明
Player12, 3, 5, 6检测敌人、环境、拾取物、触发器
Enemy21, 3检测玩家和环境
Environment3被动 — 由其他对象检测
Projectile42, 3命中敌人和墙壁
Pickup5被动 — 由玩家检测
Trigger61仅检测玩家

俯视角 / Roguelike

对象 Layer Mask 说明
Player12, 3, 5, 6检测敌人、墙壁、NPC、传感器
Enemy21, 3检测玩家和墙壁
Wall3被动
Bullet41, 2, 3命中玩家、敌人和墙壁
NPC53仅与墙壁碰撞
Sensor61检测玩家进入

Area2D / Area3D 图层

Area 使用相同的图层/掩码系统。此外,它们还有两个开关属性:

信号:area_entered vs body_entered

当一个 PhysicsBody(CharacterBody、RigidBody、StaticBody)进入 Area 时,body_entered 触发。当另一个 Area 进入时,area_entered 触发。请确保为你的用例连接正确的信号。

# Pickup Area2D — detect when the Player body enters
func _ready() -> void:
    body_entered.connect(_on_body_entered)

func _on_body_entered(body: Node2D) -> void:
    if body.is_in_group("player"):
        collect()
        queue_free()

RayCast 碰撞掩码

RayCast 节点只有 掩码(Mask)(没有图层),因为它们是检测器,而不是物理体。设置掩码以过滤射线可以命中的目标:

# RayCast that only detects enemies (layer 2)
$RayCast2D.collision_mask = 2  # Layer 2 = Enemy
# Or use the value-based API:
$RayCast2D.set_collision_mask_value(1, false)  # Ignore Player
$RayCast2D.set_collision_mask_value(2, true)   # Detect Enemy
$RayCast2D.set_collision_mask_value(3, false)  # Ignore Environment

# Line-of-sight ray that ignores projectiles
$LineOfSight.collision_mask = 0
$LineOfSight.set_collision_mask_value(1, true)  # Player
$LineOfSight.set_collision_mask_value(3, true)  # Environment

实战示例:Player – Enemy – Projectile

以下是一个典型动作游戏的完整图层设置。注释说明了每个选择背后的理由:

# Player (CharacterBody2D)
# Layer: 1 (Player)          — "I am the player"
# Mask: 2 (Enemy), 3 (Environment), 5 (Pickup)
#   — I collide with enemies, walls, and can pick up items

# Enemy (CharacterBody2D)
# Layer: 2 (Enemy)            — "I am an enemy"
# Mask: 1 (Player), 3 (Environment)
#   — I collide with the player and walls

# Player Bullet (Area2D)
# Layer: 4 (Projectile)       — "I am a projectile"
# Mask: 2 (Enemy), 3 (Environment)
#   — I hit enemies and walls, but NOT the player who fired me

# Enemy Bullet (Area2D)
# Layer: 4 (Projectile)       — "I am a projectile"
# Mask: 1 (Player), 3 (Environment)
#   — I hit the player and walls, but NOT the enemy who fired me

提示:防止友军伤害

注意,玩家子弹的掩码包含图层 2(Enemy)但不包含图层 1(Player),而敌人子弹的掩码包含图层 1(Player)但不包含图层 2(Enemy)。这就是仅通过图层配置来防止友军伤害的方式 — 无需任何代码。

调试技巧

Godot 3 → 4 迁移变化

Godot 3 Godot 4
set_collision_layer_bit(bit, value) set_collision_layer_value(layer, value)
set_collision_mask_bit(bit, value) set_collision_mask_value(layer, value)
bit 参数从 0 开始 layer 参数从 1 开始
可用 20 个图层 可用 32 个图层

迁移陷阱

如果你正在移植 Godot 3 项目,请记住 set_collision_layer_bit(0, true) 会变成 set_collision_layer_value(1, true)。索引偏移了 +1。忽略这一点,你所有的图层都会错位一位。

常见错误

1. 忘记设置掩码

你的对象有图层,但掩码为空(全为零)。该对象在物理世界中存在,却不检测任何东西。掩码匹配的其他对象仍然能检测到,但该对象上的 move_and_slide() 会穿过一切。

2. 混淆图层与掩码

把玩家的图层(Layer)而非掩码(Mask)设为 2(Enemy)。现在从物理引擎的角度看,玩家就是一个敌人。永远记住:图层 = 我是什么,掩码 = 我扫描什么。

3. 用位掩码值代替图层编号

set_collision_layer_value(4, true) 时以为它设置的是位掩码值 4(图层 1+2)。实际上,它启用的是图层 4。基于值的 API 接受的是图层编号,而不是位值。

4. 期望双向检测却只得到单向

对象 A 的掩码包含 B 的图层,但 B 的掩码不包含 A 的图层。A 上的 move_and_slide() 会与 B 碰撞,但 B 上的 move_and_slide() 会穿过 A。要让两个 CharacterBody 节点相互阻挡,双方都需要把对方的图层放进自己的掩码。

用 Godot MCP Pro 自动化物理设置

别再手动切换图层复选框了。让 AI 在几秒内配置好你的整个碰撞设置 — 包括为图层命名、分配掩码以及添加射线投射。

获取 Godot MCP Pro — $15
相关工具: setup_collision set_physics_layers get_physics_layers get_collision_info add_raycast