Collision Layers e Masks no Godot 4 Explicados

Entenda de vez collision layers godot e masks no Godot 4: a diferenca entre estar numa camada e detectar camadas, com exemplos de player, inimigo e projetil.
Se voce ja passou minutos olhando para uma Area2D que simplesmente nao dispara nenhum sinal, ou um inimigo que atravessa a parede como se ela nao existisse, a causa quase sempre e a mesma: configuracao errada de collision layers godot e masks. As duas propriedades parecem iguais nas primeiras horas de uso, sao numeradas do mesmo jeito e ficam lado a lado no Inspector, mas fazem coisas diferentes. Entender essa diferenca resolve a maior parte dos bugs de colisao que aparecem em projetos 2D.
Collision Layers e Masks no Godot 4 Explicados
A confusao tem uma origem boa: tanto layer quanto mask usam o mesmo conjunto de 32 camadas (bits) e a mesma grade de quadradinhos no Inspector. O que muda e o significado de marcar um quadrado em cada uma. Uma controla onde o objeto aparece para os outros, a outra controla o que esse objeto consegue enxergar.
A diferenca entre layer e mask
Pense em duas perguntas separadas que todo corpo fisico responde:
- Collision Layer: em qual camada eu ESTOU? Ou seja, em qual frequencia eu transmito a minha presenca.
- Collision Mask: quais camadas eu ESCUTO? Ou seja, com o que eu colido ou detecto.
A regra de ouro: um objeto A interage com um objeto B somente se a mask de A inclui alguma layer de B. A deteccao nao precisa ser reciproca. A pode detectar B sem B detectar A, basta que a mask de A contenha a layer de B.
Um erro comum e achar que marcar a layer 1 nos dois objetos ja faz eles colidirem. Nao faz. Estar na mesma camada nao significa nada se nenhum dos dois tem a camada do outro na sua mask. Layer e o que voce emite, mask e o que voce recebe.
Vale lembrar que esse sistema vale para qualquer corpo fisico: CharacterBody2D, RigidBody2D, StaticBody2D e tambem Area2D. A Area2D tem um detalhe a mais que veremos adiante, porque ela ainda separa o que detecta de corpos do que detecta de outras areas.
Nomeie as camadas no Project Settings
Trabalhar com "layer 3" e "mask 5" vira um pesadelo de memoria depois de algumas semanas. O Godot deixa voce dar nomes as camadas, e isso muda a clareza do projeto inteiro.
Va em Project, Project Settings, aba Layer Names, secao 2D Physics. La aparecem as 32 camadas vazias. Preencha as que voce vai usar. Um esquema que funciona bem para a maioria dos jogos 2D:
- Camada 1: player
- Camada 2: inimigo
- Camada 3: parede (ambiente solido)
- Camada 4: item (coletaveis)
- Camada 5: projetil_player
- Camada 6: projetil_inimigo
Depois de nomear, o Inspector passa a mostrar esses nomes ao passar o mouse sobre os quadradinhos da grade, em vez de apenas numeros. Voce continua marcando os mesmos bits, mas agora sabe o que cada um significa sem abrir uma planilha mental.
Exemplos praticos com player, inimigo, parede e item
Vamos montar a configuracao das entidades usando o esquema acima. A logica e sempre a mesma: defina a layer pelo que a entidade E, e a mask pelo que ela precisa NOTAR.
O player:
- Layer: player (1)
- Mask: parede (3), inimigo (2), item (4)
Ou seja, o player se anuncia como player e quer colidir com paredes, encostar em inimigos e coletar itens.
O inimigo:
- Layer: inimigo (2)
- Mask: parede (3), player (1)
O inimigo anda pelo mundo solido e precisa notar o player para atacar, mas nao precisa reagir a itens.
A parede (StaticBody2D):
- Layer: parede (3)
- Mask: vazia
Esse e o ponto que mais gera duvida. A parede nao precisa de mask nenhuma. Ela so precisa ESTAR na camada parede para que player e inimigo, que tem parede na mask deles, batam nela. A parede em si nao sai detectando ninguem, entao deixar a mask vazia e o correto e economiza checagens.
O item, normalmente uma Area2D:
- Layer: item (4)
- Mask: player (1)
O item espera o player encostar. Como ele e uma Area2D, ele detecta a entrada do corpo do player e dispara o sinal de coleta.
extends Area2D
signal coletado(quantidade: int)
@export var quantidade: int = 1
func _ready() -> void:
body_entered.connect(_on_body_entered)
func _on_body_entered(body: Node2D) -> void:
if body.is_in_group("player"):
coletado.emit(quantidade)
queue_free()
Repare que mesmo dentro do _on_body_entered ainda checamos o grupo. A mask ja garante que so o player vai disparar o sinal, mas o grupo deixa o codigo explicito e protege contra mudancas futuras de configuracao. Se quiser entender melhor quando usar uma area em vez de um corpo solido, vale a leitura sobre Area2D vs Body no Godot.
Projeteis e a separacao por dono
Projeteis sao o melhor exemplo de por que voce separa camadas por dono. Um tiro do player nao deve acertar o proprio player, e um tiro do inimigo nao deve acertar outro inimigo.
O projetil do player:
- Layer: projetil_player (5)
- Mask: inimigo (2), parede (3)
Ele aparece na camada de projetil do player e so colide com inimigos e paredes. Nunca toca no player nem em outros projeteis. O projetil do inimigo faz o espelho:
- Layer: projetil_inimigo (6)
- Mask: player (1), parede (3)
Com isso, fogo amigo deixa de existir sem precisar de um unico if no codigo de dano. A propria fisica filtra quem encosta em quem. Esse tipo de divisao tambem evita a tentacao de resolver tudo com checagem de grupo no momento do impacto, que e mais lento e mais facil de errar. Para um panorama de como o motor resolve esses contatos por baixo, da uma olhada em fisica de jogos no Godot.
Por que a Area nao detecta nada
A causa numero um de "minha Area2D nao detecta nada" e a mask vazia ou apontando para a camada errada. Lembre: a Area2D detecta os corpos cuja layer esta na MASK da area. Se a area de coleta tem mask vazia, ela nunca vai disparar body_entered, por mais que o player passe por cima dela.
A confusao acontece porque parece intuitivo marcar a layer da area e esperar deteccao. Mas a layer da area so serve para que OUTROS objetos detectem a area. Para a area detectar alguem, e a mask que conta.
Existe ainda a separacao entre corpos e areas dentro da propria Area2D. Os sinais body_entered e body_exited reagem a CharacterBody2D, RigidBody2D e StaticBody2D. Para detectar outra Area2D, voce usa area_entered e area_exited. As duas leituras respeitam a mask, mas sao sinais diferentes. Conectar body_entered esperando pegar uma area que entrou e outro motivo silencioso de "nada acontece".
Um checklist rapido quando uma area nao responde:
- A mask da area inclui a layer do objeto que deveria ser detectado?
- O objeto detectado tem a layer correta marcada?
- Voce conectou o sinal certo (
body_enteredpara corpos,area_enteredpara areas)? - O
Monitoringda area esta ligado? Se estiver desligado, ela nao detecta nada.
Conferindo e mudando layers por codigo
Voce nao precisa decorar quais bits estao ligados. O Godot 4 oferece metodos que trabalham com o numero da camada (de 1 a 32), batendo direto com o que aparece no Inspector.
Para ler e escrever a mask de um corpo:
extends CharacterBody2D
func _ready() -> void:
# liga a camada 2 (inimigo) na mask deste corpo
set_collision_mask_value(2, true)
# confere se a camada 3 (parede) esta na mask
var colide_com_parede: bool = get_collision_mask_value(3)
print("colide com parede: ", colide_com_parede)
# este corpo passa a ESTAR na camada 1 (player)
set_collision_layer_value(1, true)
O par equivalente para Area2D e identico, ja que ambos herdam de CollisionObject2D. Isso e util quando o jogo muda o comportamento em tempo de execucao. Um exemplo classico e o frame de invulnerabilidade depois de tomar dano: voce desliga temporariamente a deteccao de projeteis inimigos.
extends CharacterBody2D
@export var tempo_invulneravel: float = 0.8
func levar_dano() -> void:
# para de detectar projeteis inimigos (camada 6) por um instante
set_collision_mask_value(6, false)
await get_tree().create_timer(tempo_invulneravel).timeout
set_collision_mask_value(6, true)
Esse padrao deixa o sistema de invulnerabilidade no proprio motor de fisica, sem precisar de uma flag booleana checada em todo frame. Quando a mask volta, o player passa a ser atingivel de novo de forma automatica.
Se voce trabalha com deteccao direcional, por exemplo um sensor de chao ou de parede, os mesmos conceitos de layer e mask se aplicam ao RayCast2D atraves da propriedade collision_mask. O raio so detecta corpos cuja layer esta na mask dele. Tem um material dedicado a esse caso no texto sobre RayCast2D no Godot.
Resumo mental que evita os bugs
Quando algo nao colide ou nao e detectado, faca duas perguntas, sempre nessa ordem. Primeiro: o objeto que deveria ser notado esta na layer certa? Segundo: o objeto que deveria notar tem essa layer na mask? Se as duas respostas forem sim e ainda assim nada acontece, o problema esta em outro lugar (sinal errado, monitoring desligado, ou o no nem entrou na arvore).
Layer e mask nao sao duplicatas. Layer e a sua identidade fisica, o canal em que voce existe. Mask e a sua atencao, o conjunto de canais que voce escuta. Nomeie as camadas no Project Settings, separe projeteis por dono, deixe paredes sem mask, e a maior parte dos bugs de colisao some antes mesmo de virar bug. Para fixar como cada no se encaixa nesse sistema, o guia de nodes do Godot explicados ajuda a ver onde CharacterBody2D, Area2D e StaticBody2D entram na arvore.


