O que a Camera2D faz e por que usar limites
A Camera2D é o nó responsável por definir qual parte do mundo 2D será exibida na tela. Em um jogo com personagem controlável, o padrão é a câmera seguir o player para mantê-lo visível e centralizado. Sem configuração, isso pode gerar problemas comuns: “tremedeira” (jitter) ao seguir o corpo físico, câmera mostrando áreas fora da fase e sensação de movimento “duro” sem suavização.
Neste capítulo você vai: adicionar uma Camera2D ao player, configurar smoothing (suavização), definir limites de fase (limit_left/right/top/bottom), ajustar zoom, criar uma área de confinamento por sala/fase e adaptar a câmera para diferentes resoluções sem quebrar o enquadramento.
1) Adicionando Camera2D ao Player
Estrutura recomendada
Dentro da cena do player (por exemplo, um CharacterBody2D), adicione um nó filho Camera2D. Isso garante que a câmera acompanhe a posição do player automaticamente.
- No player, adicione:
Camera2D - Marque
Enabled(Godot 4) para a câmera ficar ativa - Se houver mais de uma câmera na cena, garanta que apenas uma esteja habilitada por vez
Centralização e offset
Por padrão, a câmera centraliza no seu próprio global_position. Como ela é filha do player, ela seguirá o player. Se você quiser enquadrar um pouco “à frente” (ex.: em jogos de corrida), use offset. Para jogos de plataforma, normalmente deixe offset em (0, 0) para manter o player centralizado.
2) Suavização (Smoothing) para evitar movimento duro
O smoothing faz a câmera “atrasar” levemente e interpolar o movimento, deixando a sensação mais natural. Na Godot 4, a Camera2D possui opções de suavização que podem variar por versão, mas a ideia é a mesma: habilitar suavização e ajustar a velocidade.
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
Configuração prática
- Ative
Position Smoothing(ou propriedade equivalente) - Ajuste o valor de
Smoothing Speed(ex.: 5 a 12) até ficar agradável
Se você notar jitter, o problema geralmente não é “falta de smoothing”, e sim descompasso entre física e renderização. Uma prática comum é garantir que o movimento do player aconteça no passo de física e que a câmera acompanhe de forma consistente (a câmera como filha do player já ajuda). Em casos mais chatos, você pode experimentar ativar o modo de processo apropriado da câmera (quando disponível) para acompanhar no mesmo ritmo do movimento.
3) Limites da câmera (limit_left/right/top/bottom)
Os limites impedem a câmera de mostrar fora da área jogável. Isso é essencial em fases com bordas definidas (TileMap, paredes, etc.).
Como pensar os limites
Os limites são valores em coordenadas do mundo (pixels/unidades 2D) que restringem o centro da câmera. Em geral, você define:
limit_left: menor X permitidolimit_right: maior X permitidolimit_top: menor Y permitidolimit_bottom: maior Y permitido
Esses valores devem corresponder ao retângulo interno da fase (ou sala) onde a câmera pode se mover.
Definindo limites manualmente (rápido para testar)
No Inspector da Camera2D, procure a seção de limites e preencha valores. Exemplo (fase 2000x1000 começando em (0,0)):
limit_left = 0
limit_top = 0
limit_right = 2000
limit_bottom = 1000Se sua fase começa em outra posição (ex.: sala deslocada), use as coordenadas reais do mundo.
4) Zoom: aproximar/afastar sem distorcer o jogo
O zoom da Camera2D altera a escala da visão. Valores menores aproximam (ex.: (0.8, 0.8)), valores maiores afastam (ex.: (1.2, 1.2)).
Boas práticas
- Mantenha
zoom.xezoom.yiguais para não “esticar” - Se você usa pixel art, combine zoom com configurações de projeto para evitar blur (filtro) e garantir pixels nítidos
- Ao mudar o zoom, os limites continuam em coordenadas do mundo, mas a área visível muda; teste bordas para não revelar fora da fase
5) Área de confinamento por fase/sala (limites dinâmicos)
Em jogos com múltiplas salas, é comum cada sala ter seus próprios limites. Em vez de setar limites fixos na câmera, você pode criar uma “área de câmera” por sala e, quando o player entra nela, atualizar os limites da câmera automaticamente.
Abordagem recomendada: CameraBounds com Area2D
Crie um nó por sala chamado, por exemplo, CameraBounds, contendo um Area2D com um CollisionShape2D retangular cobrindo a sala. Quando o player entrar, a câmera passa a usar aquele retângulo como limites.
Passo a passo
- Na cena da fase, crie
Area2D(nome:CameraBounds) - Adicione
CollisionShape2DcomRectangleShape2D - Ajuste o retângulo para cobrir a área jogável da sala
- Configure a camada/máscara para detectar o player (sem interferir em colisão física; é apenas detecção)
Script para aplicar limites ao entrar
Anexe este script ao CameraBounds. Ele calcula os limites com base no retângulo do CollisionShape2D e aplica na câmera do player.
extends Area2D
@export var camera_path: NodePath
@onready var shape := $CollisionShape2D.shape as RectangleShape2D
func _ready() -> void:
body_entered.connect(_on_body_entered)
func _on_body_entered(body: Node) -> void:
if not body.is_in_group("player"):
return
var cam := get_node_or_null(camera_path) as Camera2D
if cam == null:
# Alternativa: pegar a câmera como filha do player
cam = body.get_node_or_null("Camera2D") as Camera2D
if cam == null:
return
# Retângulo do bounds em coordenadas globais
var half := shape.size * 0.5
var center := global_position
var left := int(center.x - half.x)
var right := int(center.x + half.x)
var top := int(center.y - half.y)
var bottom := int(center.y + half.y)
cam.limit_left = left
cam.limit_right = right
cam.limit_top = top
cam.limit_bottom = bottomImportante: para o exemplo acima funcionar, você precisa de uma forma consistente de achar a câmera. Duas opções simples:
- Definir
camera_pathapontando para aCamera2Ddo player (ou uma câmera global) - Garantir que a câmera se chame
Camera2De seja filha do player
Como garantir transição suave entre salas
Ao trocar limites, a câmera pode “pular” se o player estiver perto da borda. Para suavizar:
- Mantenha o smoothing ativo
- Evite trocar limites a cada frame; troque apenas em eventos (entrada/saída)
- Se duas áreas se sobrepõem, defina uma prioridade (ex.: sala atual vence) para não ficar alternando
6) Ajustando a câmera para diferentes resoluções (sem quebrar o enquadramento)
O objetivo é manter uma área de jogo consistente em diferentes telas. Você pode fazer isso combinando: (1) configurações de janela/viewport do projeto e (2) zoom controlado.
Estratégia prática: “resolução base” + zoom adaptativo
Defina uma resolução base (ex.: 320x180, 640x360, 1280x720) e calcule um zoom para caber na tela real mantendo proporção. Assim você preserva o campo de visão e evita que telas maiores mostrem “mais fase” do que o planejado.
Exemplo: script no player (ou em um nó gerenciador) para ajustar o zoom da câmera com base no tamanho da janela, mantendo a proporção da resolução base:
@export var base_resolution := Vector2(640, 360)
@onready var cam := $Camera2D
func _ready() -> void:
_update_camera_zoom()
get_viewport().size_changed.connect(_update_camera_zoom)
func _update_camera_zoom() -> void:
var viewport_size := get_viewport().get_visible_rect().size
var scale_x := viewport_size.x / base_resolution.x
var scale_y := viewport_size.y / base_resolution.y
var scale := min(scale_x, scale_y)
# zoom maior = mais afastado; aqui queremos “encaixar” a base
# então usamos o inverso do scale
var z := 1.0 / max(scale, 0.001)
cam.zoom = Vector2(z, z)Com isso, você mantém o mesmo “enquadramento lógico” em diferentes resoluções. Teste em janelas com proporções diferentes (16:9, 16:10, ultrawide) e decida se prefere cortar (letterbox) ou mostrar mais cenário; o zoom adaptativo acima tende a preservar a área base, podendo sobrar espaço em uma dimensão dependendo do modo de estiramento do projeto.
7) Evitando jitter (tremedeira) ao manter o player centralizado
Jitter costuma aparecer quando o player se move em passos de física e a câmera tenta seguir em passos diferentes, ou quando há arredondamento/escala fracionária (especialmente com pixel art). Checklist rápido:
- Deixe a câmera como filha do player (reduz discrepâncias)
- Use smoothing moderado (nem 0, nem exagerado)
- Evite zoom fracionário em pixel art se estiver causando tremulação; prefira valores que resultem em pixels inteiros na tela (dependendo do seu setup)
- Se estiver usando TileMap/pixel art, revise configurações de filtro e snapping do projeto para evitar blur e subpixel
Exercício: duas salas com transição de câmera (atualizando limites)
Objetivo
Criar duas salas lado a lado. Ao atravessar a porta/corredor, a câmera deve atualizar os limites para a sala atual, mantendo o player centralizado e sem jitter perceptível.
Requisitos do exercício
- Duas áreas retangulares (Sala A e Sala B) com tamanhos diferentes
- Cada sala tem um
CameraBounds(Area2D + CollisionShape2D retangular) - Ao entrar na sala, atualizar
limit_left/right/top/bottomda câmera - Smoothing ligado para transição suave
- Player permanece centralizado na maior parte do tempo (exceto quando encostado nos limites)
Passo a passo sugerido
- Monte a Sala A e Sala B no mesmo mundo (por exemplo, Sala B à direita da Sala A)
- Crie um corredor/porta entre elas (pode ser apenas um espaço livre)
- Adicione
CameraBoundsna Sala A cobrindo sua área jogável - Adicione
CameraBoundsna Sala B cobrindo sua área jogável - Garanta que o player esteja no grupo
player(Inspector > Node > Groups) - Em cada
CameraBounds, configurecamera_pathpara apontar para aCamera2Ddo player (ou deixe vazio e use a busca por filhoCamera2D) - Ative smoothing na câmera e ajuste a velocidade
- Teste atravessando de A para B e de B para A
Desafio extra (para eliminar alternância em áreas próximas)
Se a porta for estreita e as áreas encostarem, você pode entrar e sair rapidamente do Area2D, causando troca repetida de limites. Resolva adicionando prioridade:
- Adicione
@export var priority := 0em cadaCameraBounds - No player, mantenha a referência do bounds atual e só aceite trocar se a prioridade for maior (ou se o atual for nulo)
Exemplo de lógica no player (opcional):
# No Player.gd
var current_bounds_priority := -999
func apply_camera_limits(cam: Camera2D, left: int, right: int, top: int, bottom: int, priority: int) -> void:
if priority < current_bounds_priority:
return
current_bounds_priority = priority
cam.limit_left = left
cam.limit_right = right
cam.limit_top = top
cam.limit_bottom = bottomDaí o CameraBounds chamaria apply_camera_limits ao invés de setar direto. Isso evita “piscar” de limites quando duas áreas competem.