Area2D vs CharacterBody2D no Godot: qual usar em cada situação

Area2D no Godot detecta, CharacterBody2D colide. Entenda a diferença na prática, com código GDScript e casos de uso reais para escolher certo.
Area2D vs CharacterBody2D no Godot: qual usar em cada situação
Essa dúvida aparece em todo projeto de iniciante: a moeda do jogo deve ser uma Area2D ou um CharacterBody2D? E o projétil? E a zona de dano? Quem escolhe errado descobre do pior jeito, com o player travando em cima da moeda em vez de coletar, ou a bala empurrando o inimigo pela tela como se fosse um carrinho de supermercado.
A resposta curta cabe numa frase: Area2D detecta, body colide. Area2D é um sensor, sabe quando algo entrou e saiu dela, mas não bloqueia nada. CharacterBody2D (e os outros bodies) ocupa espaço físico de verdade: para, empurra, desliza, é parado.
A resposta longa é este artigo. Vou mostrar o que cada um faz por baixo, o código de cada caso, e uma regra de bolso pra você nunca mais hesitar na hora de criar a cena.
A diferença fundamental: detecção vs física
O Godot separa o mundo 2D em duas categorias de objeto que participam de colisão:
Bodies (PhysicsBody2D) são objetos sólidos. StaticBody2D, CharacterBody2D, RigidBody2D e AnimatableBody2D herdam todos de PhysicsBody2D. Quando dois bodies se encontram, a engine resolve a colisão: um para, desliza ou empurra o outro. Eles não se atravessam.
Area2D é uma região do espaço. Ela não tem massa, não bloqueia movimento, não participa da resolução física. O único trabalho dela é responder duas perguntas: "alguém entrou aqui?" e "alguém saiu daqui?". Ela faz isso emitindo sinais.
Os dois usam CollisionShape2D pra definir o formato, e os dois respeitam collision layers e masks. Por isso a confusão: no editor eles parecem quase iguais. A diferença está no que acontece no contato. Body resiste, Area2D só avisa.
Uma analogia que funciona: body é a parede da loja, Area2D é o sensor da porta automática. A parede te impede de passar. O sensor não te segura, mas dispara um evento quando você cruza.
O que isso significa em custo
Tem uma consequência de performance embutida nessa diferença. Resolver colisão física é caro: a engine precisa calcular ponto de contato, normal, resposta, e fazer isso de forma estável a cada tick de física. Detectar overlap é bem mais barato, é basicamente um teste de "essas duas shapes se sobrepõem?".
Então além de ser a ferramenta semanticamente certa, Area2D é a ferramenta mais leve sempre que você só precisa saber que algo encostou, sem precisar que algo seja bloqueado.
Quando usar Area2D no Godot
Use Area2D pra tudo que é gatilho, sensor ou zona. Os casos clássicos:
- Itens coletáveis: moeda, vida, power-up. O player atravessa, o item some, o contador sobe.
- Zonas de dano: espinho, lava, área de veneno. Encostou, perdeu vida, mas nada física e fisicamente segura o player ali.
- Hitbox e hurtbox de ataque: a área da espada durante o golpe, a área vulnerável do inimigo.
- Gatilhos de level: porta de transição de fase, checkpoint, ponto que dispara cutscene.
- Campo de visão de inimigo: um cone ou círculo na frente do inimigo que detecta o player.
- Projéteis simples: bala que voa reto e some no primeiro contato não precisa de física, precisa de detecção.
O padrão de código é sempre o mesmo: conectar o sinal body_entered (quando um body entra na área) ou area_entered (quando outra Area2D entra). Uma moeda completa:
extends Area2D
func _ready():
body_entered.connect(_on_body_entered)
func _on_body_entered(body):
if body.is_in_group("player"):
body.adicionar_moeda()
queue_free()
Três detalhes que valem destacar nesse código:
- O sinal dispara com qualquer body que entre na mask da moeda. O filtro por grupo (
is_in_group("player")) garante que só o player coleta. Dá pra fazer o mesmo filtro só com layers e masks, deixando a moeda detectar apenas a camada do player, que é ainda mais limpo. queue_free()remove a moeda no fim do frame, do jeito seguro. Nunca delete um node no meio de um callback de física comfree()direto.- A moeda não tem um único pixel de colisão sólida. O player passa por cima dela sem desviar de trajetória, que é exatamente o comportamento esperado.
E o caso da zona de dano contínuo (lava, veneno), onde você quer machucar enquanto o player está dentro, não só na entrada? A própria Area2D mantém a lista de quem está dentro:
extends Area2D
@export var dano_por_segundo := 10.0
func _physics_process(delta):
for body in get_overlapping_bodies():
if body.has_method("receber_dano"):
body.receber_dano(dano_por_segundo * delta)
O get_overlapping_bodies() retorna todos os bodies dentro da área naquele tick. Sem sinal, sem timer, sem estado extra pra gerenciar.
Quando usar CharacterBody2D
CharacterBody2D é pra coisa que se move por script e precisa colidir de verdade: player, inimigo que patrulha, NPC que anda. Ele não é o único body, mas é o que você mais usa, porque é o único feito pra movimento controlado por código com resolução de colisão automática.
O coração dele é o move_and_slide(): você define velocity, chama o método, e a engine move o corpo respeitando paredes, chão e teto, deslizando ao longo das superfícies em vez de travar nelas.
extends CharacterBody2D
const SPEED = 300.0
const JUMP_VELOCITY = -400.0
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")
func _physics_process(delta):
if not is_on_floor():
velocity.y += gravity * delta
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
velocity.y = JUMP_VELOCITY
var direction = Input.get_axis("ui_left", "ui_right")
if direction:
velocity.x = direction * SPEED
else:
velocity.x = move_toward(velocity.x, 0, SPEED)
move_and_slide()
Nada disso existe na Area2D. Ela não tem velocity, não tem move_and_slide(), não tem is_on_floor(). Se você tentar fazer um player com Area2D, vai acabar reimplementando colisão na mão, mal, e atravessando parede em quina.
O contrário também vale: se você fizer a moeda com CharacterBody2D, ela vira um obstáculo sólido. O player tromba nela. Já vi projeto de aluno onde o "item coletável" funcionava como degrau pra pular mais alto. Bug? Tecnicamente não, o body fez exatamente o que body faz: ocupou espaço.
Os dois juntos: o padrão que todo jogo usa
Aqui está o pulo do gato que destrava o entendimento: não é Area2D ou body, é Area2D e body na mesma cena. Quase todo personagem de jogo de verdade é um CharacterBody2D com uma ou mais Area2D como filhas.
O exemplo canônico é o inimigo de patrulha que detecta o player de longe:
Inimigo (CharacterBody2D) ← corpo sólido, anda e colide
├── CollisionShape2D ← shape física do corpo
├── Sprite2D
└── CampoDeVisao (Area2D) ← sensor, não colide
└── CollisionShape2D ← shape do alcance de detecção
O body cuida do movimento e da colisão com o cenário. A Area2D filha cuida de "o player entrou no meu alcance?". Cada node faz só o trabalho dele:
extends CharacterBody2D
const SPEED = 100.0
var alvo: Node2D = null
func _ready():
$CampoDeVisao.body_entered.connect(_on_visao_body_entered)
$CampoDeVisao.body_exited.connect(_on_visao_body_exited)
func _on_visao_body_entered(body):
if body.is_in_group("player"):
alvo = body
func _on_visao_body_exited(body):
if body == alvo:
alvo = null
func _physics_process(delta):
if alvo:
var direcao = global_position.direction_to(alvo.global_position)
velocity = direcao * SPEED
else:
velocity = Vector2.ZERO
move_and_slide()
O mesmo padrão se repete em todo lugar. O player de um jogo de ação costuma ser um CharacterBody2D com uma Area2D de hurtbox (onde ele toma dano) e outra de hitbox de ataque (que liga só durante o golpe). O carro de corrida é um body com uma Area2D na frente detectando a linha de chegada. O chefe tem áreas separadas pra cabeça (dano crítico) e corpo (dano normal).
Um cuidado importante nesse arranjo: configure a collision layer e a mask da Area2D filha separadas das do body pai. O campo de visão do inimigo só precisa detectar a camada do player; não faz sentido ele "ver" paredes e outros inimigos. Mask enxuta é menos processamento e menos filtro em código.
Regra de bolso e tabela de decisão
Quando bater a dúvida, faça uma pergunta só: esse objeto precisa bloquear ou ser bloqueado por alguma coisa?
- Sim, precisa ser sólido → é um body. Se move por script, CharacterBody2D. Se nunca se move, StaticBody2D. Se a física controla (cai, rola, quica), RigidBody2D.
- Não, só precisa saber que algo encostou → Area2D.
Casos comuns resolvidos:
| Objeto | Node certo | Por quê |
|---|---|---|
| Player, inimigo que anda | CharacterBody2D | Movimento por script com colisão sólida |
| Parede, chão, plataforma fixa | StaticBody2D | Sólido e imóvel, o mais barato |
| Caixa que tomba, barril que rola | RigidBody2D | A física controla o movimento |
| Moeda, power-up, item | Area2D | Detecta a coleta, não bloqueia |
| Espinho, lava, zona de veneno | Area2D | Aplica dano, não segura ninguém |
| Bala que some no primeiro impacto | Area2D | Só precisa detectar o acerto |
| Granada que quica antes de explodir | RigidBody2D | O quique é física de verdade |
| Porta de fim de fase, checkpoint | Area2D | Gatilho puro |
| Plataforma móvel | AnimatableBody2D | Sólida, movida por script, empurra quem está em cima |
Repare nos dois projéteis da tabela. A bala reta é Area2D porque o comportamento dela é "voou, encostou, causou dano, sumiu". A granada é RigidBody2D porque ela precisa quicar na parede e rolar no chão, e isso é resolução física que Area2D não faz. O mesmo conceito de jogo (projétil) usa nodes diferentes dependendo do comportamento. O node segue o comportamento, não a categoria do objeto.
Erros que eu vejo com frequência
Area2D como corpo do player. O sintoma é player atravessando parede ou colisão "feita na mão" cheia de caso especial. A correção é migrar pra CharacterBody2D e deixar o move_and_slide() trabalhar.
Body como item coletável. O sintoma é o player trombando em moeda. Item é Area2D, sempre.
Esquecer a CollisionShape2D. Tanto Area2D quanto body precisam de uma shape filha pra funcionar. Sem ela, o sinal nunca dispara e a colisão nunca acontece. O Godot até mostra um aviso amarelo no node, mas todo mundo ignora aviso amarelo um dia.
Sinal conectado no node errado. body_entered é sinal da Area2D, não da CollisionShape2D. A shape só define o formato, quem emite é a área.
Mask aberta demais. Area2D detectando todas as camadas dispara sinal pra todo encostão do jogo e te obriga a filtrar tudo em código. Restrinja a mask pra camada que interessa e metade dos if somem.
Fechando
A escolha entre Area2D e CharacterBody2D para de ser dúvida quando você enxerga os papéis: body é matéria, Area2D é sensor. Um ocupa espaço e resolve colisão, o outro observa uma região e avisa o que entra e sai. E nos jogos reais eles quase sempre trabalham juntos, com o body sendo o corpo e as áreas sendo os sentidos.
Pra fixar, abra um projeto vazio e monte o trio básico: um player CharacterBody2D, uma parede StaticBody2D e uma moeda Area2D. Depois pendure um campo de visão no inimigo. São vinte minutos de prática que valem mais que qualquer releitura deste texto.


