Quick answer: Layer is what the body IS. Mask is what it looks FOR. Bodies collide when A’s mask contains B’s layer (or vice versa). Name layers in Project Settings to keep them readable.
A shooter: bullets pass through walls but enemies bounce off them. Bullets collide with enemies but also with each other. Layer/mask confusion — the classic Godot physics gotcha.
Naming Layers
Project Settings → Layer Names → 2D / 3D Physics. Rename layer 1 to “World”, layer 2 to “Enemy”, layer 3 to “Bullet”, layer 4 to “Player”. Inspector now shows these names.
Layer/Mask Setup
| Body | Layer (IS) | Mask (COLLIDES WITH) |
|---|---|---|
| Player | Player | World, Enemy |
| Enemy | Enemy | World, Player, Bullet |
| Bullet | Bullet | World, Enemy |
| Wall | World | (empty: walls don’t initiate) |
Two bodies collide if either’s mask includes the other’s layer. Bullets pass each other because neither has Bullet in its mask.
Code Setup
var bullet_layer = 1 << 2 # layer 3 (0-indexed bit 2)
var world_layer = 1 << 0
var enemy_layer = 1 << 1
bullet.collision_layer = bullet_layer
bullet.collision_mask = world_layer | enemy_layer
Or use set_collision_layer_value / set_collision_mask_value:
bullet.set_collision_layer_value(3, true)
bullet.set_collision_mask_value(1, true)
bullet.set_collision_mask_value(2, true)
1-indexed bit numbers; clearer than bit shifts.
One-Way Logic
Often only one direction matters. Bullets check for enemies; enemies don’t need to check for bullets. If both check, you may double-handle events. Pick the direction that’s easier to filter.
Verifying
Fire bullets through wall: blocked. Fire at enemy: hits. Fire near another bullet: passes through. Enable Debug → Visible Collision Shapes to see bounding shapes in play.
“Layer is identity, mask is interest. Read it once carefully and the rest falls into place.”
Sketch a simple table of body types vs layers/masks before coding combat. Saves hours of ‘why didn’t this hit’ debugging later.