Voltar para o Blog
Quest Log

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

Cena de jogo 2D mostrando um personagem colidindo com uma parede e atravessando uma zona de detecçã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:

  1. 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.
  2. queue_free() remove a moeda no fim do frame, do jeito seguro. Nunca delete um node no meio de um callback de física com free() direto.
  3. 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.

Próximo nível
Quer aprender isso na prática?

No CursoGame.Dev você sai dos tutoriais soltos e constrói jogos publicáveis, com trilha progressiva, quests práticas e feedback real.

Conhecer a plataforma
+500 alunos4.9/5Garantia 7 dias

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:

ObjetoNode certoPor quê
Player, inimigo que andaCharacterBody2DMovimento por script com colisão sólida
Parede, chão, plataforma fixaStaticBody2DSólido e imóvel, o mais barato
Caixa que tomba, barril que rolaRigidBody2DA física controla o movimento
Moeda, power-up, itemArea2DDetecta a coleta, não bloqueia
Espinho, lava, zona de venenoArea2DAplica dano, não segura ninguém
Bala que some no primeiro impactoArea2DSó precisa detectar o acerto
Granada que quica antes de explodirRigidBody2DO quique é física de verdade
Porta de fim de fase, checkpointArea2DGatilho puro
Plataforma móvelAnimatableBody2DSó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.