Quick answer: collision_layer is what an object is; collision_mask is what it looks for. An Area only reports a body when the Area's mask includes the body's layer. In code, set_collision_mask_value(n, true) uses 1-based layer numbers, while collision_mask = n sets a bitmask — mixing the two is the most common bug.
Collision layers are simple once the model clicks, but the Inspector, the bitmask properties and the helper methods use three slightly different views of the same 32 bits, and that is where most layer bugs come from.
The model: layer is identity, mask is attention
Every physics object has 32 layers. collision_layer says which layers the object lives on. collision_mask says which layers it scans. Rules worth memorising:
- Areas: an Area2D/Area3D reports a body when the area's mask contains one of the body's layers. The body's own mask does not matter for detection.
- Bodies: two bodies collide when either one's mask contains the other's layer. For predictable behaviour, set both sides anyway.
- Raycasts and ShapeCasts only use their own mask.
So a player on layer 1 and an enemy hurtbox Area on layer 3 that should detect the player needs the hurtbox's mask to include layer 1.
The 1-based vs bitmask trap
The Inspector shows layers numbered 1 to 32. The helper methods use the same numbers:
set_collision_layer_value(3, true) # layer 3
set_collision_mask_value(1, true) # scan layer 1
The raw properties are bitmasks, where layer *n* is bit *n - 1*:
collision_layer = 3 # WRONG if you meant layer 3: this is layers 1 + 2
collision_layer = 1 << 2 # layer 3 (value 4)
If an object "sometimes" collides with the wrong things, print collision_layer and collision_mask and convert them to binary.
Name your layers
Give layers names under Project Settings > Layer Names > 2D Physics (or 3D Physics). The Inspector grid then shows "player", "enemies", "world" on hover, which makes mismatches obvious. Keep a convention, for example: 1 world, 2 player, 3 enemies, 4 player projectiles, 5 pickups.
Other reasons nothing collides
- The shape is missing or disabled. A CollisionShape2D with no Shape resource, or with Disabled ticked, collides with nothing and shows a warning triangle in the scene tree.
- Monitoring is off. Areas need Monitoring on to detect anything, and the other area needs Monitorable on for area-to-area detection.
- Changing layers inside a physics callback. Changing collision properties inside
body_enteredor another physics signal can be rejected while the physics server is flushing queries. Useset_deferred():
func _on_body_entered(body):
$Hurtbox.set_deferred("monitoring", false)
- Scale on physics bodies. Non-uniform scale on a CollisionShape's parent body gives unreliable results; change the shape's size instead of scaling the node.
Verify visually
Enable Debug > Visible Collision Shapes and run the game. If shapes appear where you expect and they still do not interact, the problem is the layer/mask numbers. If a shape is missing or misplaced, the problem is the scene setup.