Por que as camadas de colisão importam
As camadas de colisão são a base de todas as interações físicas no Godot 4 — movimento, detecção de acerto, raycasting, gatilhos de área e muito mais. Entender a diferença entre Layer e Mask é crucial, e confundi-las é um dos problemas mais comuns tanto para iniciantes quanto para desenvolvedores experientes.
Layer vs Mask — O modelo mental
Todo objeto físico no Godot tem duas propriedades de bitmask:
- Layer = "Eu existo nestas camadas" (o que eu sou)
- Mask = "Eu examino estas camadas" (o que eu posso ver / atingir)
Regra fundamental
A colide com B quando a máscara de A inclui a camada de B OU a máscara de B inclui a camada de A. Basta que um dos lados "veja" o outro para que a colisão ocorra.
Nomeando camadas nas Configurações do Projeto
Antes de escrever qualquer código, nomeie suas camadas. Isso torna o Inspetor muito mais fácil de usar e evita confusão à medida que seu projeto cresce.
Configurações do Projeto > Nomes de Camadas > Física 2D (ou Física 3D):
| Camada nº | Nome | Finalidade |
|---|---|---|
| 1 | Player | O personagem do jogador |
| 2 | Enemy | Todos os corpos inimigos |
| 3 | Environment | Paredes, pisos, plataformas |
| 4 | Projectile | Balas, flechas, feitiços |
| 5 | Pickup | Itens, moedas, kits de vida |
| 6 | Trigger | Sensores, zonas de spawn, checkpoints |
Definindo camadas em código (GDScript)
O Godot 4 oferece duas abordagens — a API baseada em valores (recomendada) e a atribuição direta de bitmask:
# 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)
Pegadinha do bitmask
Os números de camada são baseados em 1 em set_collision_layer_value(), mas o bitmask subjacente é baseado em 0. Camada 1 = bit 0 = valor 1, Camada 2 = bit 1 = valor 2, Camada 3 = bit 2 = valor 4. Na dúvida, use a API baseada em valores para evitar erros.
Esquemas de camadas comuns
Plataforma / Side-scroller
| Objeto | Layer | Mask | Explicação |
|---|---|---|---|
| Player | 1 | 2, 3, 5, 6 | Detecta inimigos, ambiente, itens, gatilhos |
| Enemy | 2 | 1, 3 | Detecta o jogador e o ambiente |
| Environment | 3 | — | Passivo — detectado pelos outros |
| Projectile | 4 | 2, 3 | Atinge inimigos e paredes |
| Pickup | 5 | — | Passivo — o jogador o detecta |
| Trigger | 6 | 1 | Detecta apenas o jogador |
Top-down / Roguelike
| Objeto | Layer | Mask | Explicação |
|---|---|---|---|
| Player | 1 | 2, 3, 5, 6 | Detecta inimigos, paredes, NPCs, sensores |
| Enemy | 2 | 1, 3 | Detecta o jogador e paredes |
| Wall | 3 | — | Passivo |
| Bullet | 4 | 1, 2, 3 | Atinge o jogador, inimigos e paredes |
| NPC | 5 | 3 | Colide apenas com paredes |
| Sensor | 6 | 1 | Detecta a entrada do jogador |
Camadas de Area2D / Area3D
As Areas usam o mesmo sistema de Layer/Mask. Além disso, elas têm duas propriedades de alternância:
-
monitoring— Setrue, esta Area detecta ativamente outros objetos entrando nela. -
monitorable— Setrue, outras Areas podem detectar esta Area.
Sinais: area_entered vs body_entered
body_entered dispara quando um PhysicsBody (CharacterBody, RigidBody, StaticBody) entra na Area. area_entered dispara quando outra Area entra. Certifique-se de conectar o sinal correto para o seu caso de uso.
# 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()
Máscara de colisão do RayCast
Os nós RayCast têm apenas uma Mask (sem Layer), porque são detectores, não corpos físicos. Defina a máscara para filtrar o que o raio pode atingir:
# 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
Exemplo prático: Player – Enemy – Projectile
Aqui está uma configuração completa de camadas para um jogo de ação típico. Os comentários mostram o raciocínio por trás de cada escolha:
# 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
Dica: prevenção de fogo amigo
Observe que as balas do jogador mascaram a camada 2 (Enemy), mas não a camada 1 (Player), e as balas dos inimigos mascaram a camada 1 (Player), mas não a camada 2 (Enemy). É assim que você evita o fogo amigo apenas por meio da configuração de camadas — sem necessidade de código.
Dicas de depuração
- Formas de colisão visíveis: No editor, vá em Depurar > Formas de Colisão Visíveis para renderizar todas as formas de colisão em tempo de execução. Isso revela imediatamente colisores ausentes ou desalinhados.
- Verificação no Inspetor: Selecione um nó e expanda Collision > Layer e Collision > Mask no Inspetor. Passe o mouse sobre cada bit para ver seu nome (se você os nomeou nas Configurações do Projeto).
-
Imprimir em tempo de execução:
print("Layer: ", collision_layer, " Mask: ", collision_mask)para verificar os valores de bitmask durante o jogo.
Mudanças de migração do 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) |
O parâmetro bit era baseado em 0
|
O parâmetro layer é baseado em 1
|
| 20 camadas disponíveis | 32 camadas disponíveis |
Armadilha da migração
Se você está portando um projeto do Godot 3, lembre-se de que set_collision_layer_bit(0, true) se torna set_collision_layer_value(1, true). O índice muda em +1. Ignore isso e todas as suas camadas ficarão deslocadas em uma posição.
Erros comuns
1. Esquecer de definir a Mask
Seu objeto tem uma Layer, mas a Mask está vazia (tudo zero). O objeto existe no mundo físico, mas não detecta nada. Outros objetos com máscaras correspondentes ainda vão detectá-lo, mas o move_and_slide() deste objeto atravessará tudo.
2. Confundir Layer com Mask
Definir a Layer do jogador como 2 (Enemy) em vez de sua Mask. Agora o jogador é um inimigo do ponto de vista do motor de física. Lembre-se sempre: Layer = o que eu sou, Mask = o que eu examino.
3. Usar valores de bitmask em vez de números de camada
Escrever set_collision_layer_value(4, true) achando que isso define o valor de bitmask 4 (camadas 1+2). Na verdade, ele habilita a camada 4. A API baseada em valores recebe números de camada, não valores de bit.
4. Detecção unidirecional quando se espera bidirecional
O objeto A mascara a camada de B, mas B não mascara a camada de A. O move_and_slide() de A colidirá com B, mas o move_and_slide() de B atravessará A. Para que dois nós CharacterBody bloqueiem um ao outro, ambos precisam ter a camada do outro em sua máscara.
Automatize a configuração de física com o Godot MCP Pro
Pare de alternar caixas de seleção de camadas manualmente. Deixe a IA configurar toda a sua configuração de colisão em segundos — incluindo nomear camadas, atribuir máscaras e adicionar raycasts.
Obter o Godot MCP Pro — $15setup_collision
set_physics_layers
get_physics_layers
get_collision_info
add_raycast