Voltar para o Blog
Quest Log

Cinemachine 2D: Câmera que Segue o Jogador com Limites

Personagem 2D num cenário de plataforma com um retângulo de câmera acompanhando e uma borda marcando os limites da fase

Unity Cinemachine 2D na prática: Position Composer com dead zone e lookahead, Confiner 2D para não mostrar fora do mapa e troca de câmera por sala.

Ferramenta:Unity

Cinemachine 2D: Câmera que Segue o Jogador com Limites

Para fazer uma câmera 2D que segue o jogador com Unity Cinemachine 2D, você cria uma CinemachineCamera pelo atalho de câmera 2D, coloca o jogador no Tracking Target e deixa o Position Composer cuidar do enquadramento. Os limites do mapa vêm de uma extensão, o Confiner 2D, que lê um collider desenhado em volta da fase. Sem escrever uma linha de código, você já tem uma câmera que acompanha, respira e não mostra o vazio fora do cenário.

No fim deste guia você vai ter: dead zone e damping ajustados para plataforma, lookahead que olha para onde o jogador corre, confiner que respeita a borda da fase, troca de câmera por sala com um script curto e a combinação com Pixel Perfect sem tremedeira. Tudo com o Cinemachine 3, que é a versão que roda no Unity 6.3 LTS.

Unity Cinemachine 2D: configuração base

O Cinemachine é um pacote. No Unity 6.3, abra Window > Package Management > Package Manager, vá em Unity Registry, procure Cinemachine e instale. A documentação oficial atual é a da linha 3.1 do pacote. Se você estiver seguindo um tutorial que fala em CinemachineVirtualCamera ou em "Framing Transposer", ele é do Cinemachine 2: os conceitos ajudam, os nomes não batem.

Com o pacote instalado:

  1. Na Hierarchy, use GameObject > Cinemachine > Targeted Cameras > 2D Camera. A Unity cria uma CinemachineCamera já com o Position Composer selecionado e adiciona um Cinemachine Brain na Main Camera, se ainda não tiver.
  2. Na CinemachineCamera, arraste o objeto do jogador para Tracking Target.
  3. Na seção Lens, ajuste o Orthographic Size. A Main Camera precisa estar com Projection em Orthographic. Daqui em diante, quem manda no tamanho e na posição é a CinemachineCamera, não a Main Camera. Mexer no Transform da Main Camera não adianta nada: o Brain sobrescreve a cada frame.

Um detalhe que confunde quem chega do 3D: no Position Composer existe o campo Camera Distance. Em câmera ortográfica, a distância no eixo Z não muda o tamanho do que aparece na tela. O zoom é o Orthographic Size. Deixe o Camera Distance num valor que mantenha a câmera na frente dos sprites (10 é o padrão confortável) e esqueça ele.

Position Composer: dead zone e damping

O Position Composer move a câmera para manter o alvo num ponto da tela. Ele não gira a câmera, só translada, o que é exatamente o que um jogo 2D precisa. A documentação do pacote descreve o componente como bom para câmeras 2D e ortográficas.

As propriedades que importam para um jogo de plataforma:

  • Screen Position: onde o jogador fica na tela. O centro é (0, 0). Em plataforma com câmera horizontal, deixar o jogador um pouco abaixo do centro mostra mais céu e mais plataformas acima, que é onde o jogador vai olhar ao pular.
  • Dead Zone: um retângulo, em fração da tela, dentro do qual o jogador anda sem arrastar a câmera. Um Dead Zone de 0.1 de largura por 0.2 de altura evita que a câmera suba e desça a cada pulinho.
  • Hard Limits: a borda que o jogador nunca ultrapassa. Se a câmera estiver atrasada por causa do damping, ela é puxada na hora quando o alvo encosta nesse limite. Isso evita o personagem sair da tela numa queda longa.
  • Damping: tempo de resposta por eixo (X, Y, Z). Valores de 0.5 a 1 no X e 1 a 2 no Y funcionam bem para plataforma: a câmera segue de lado com firmeza e é mais preguiçosa na vertical.

Por que a vertical mais lenta? Porque o jogador pula muito. Se a câmera acompanhar cada pulo na mesma velocidade, a tela inteira balança e o jogo cansa. A técnica é a mesma que eu explico no post de câmera 2D na Godot com drag margins: a engine muda, o raciocínio não.

Lookahead na direção do movimento

O lookahead adianta o enquadramento para o lado em que o jogador está indo. Num runner ou num plataforma rápido, isso dá ao jogador tempo de ver o buraco antes de cair nele.

No Position Composer:

  • Lookahead Time: quantos segundos à frente a câmera tenta prever a posição do alvo. Comece com 0.3.
  • Lookahead Smoothing: suaviza a previsão. Sem isso, cada troca de direção dá um tranco. Valores entre 5 e 10 tiram o tranco.
  • Lookahead Ignore Y: marque em jogo de plataforma. Sem esse checkbox, a câmera tenta prever a queda e o pulo, e a tela "despenca" antes do jogador.

O lookahead brigando com a dead zone é um erro comum: se a dead zone for larga e o lookahead alto, a câmera fica parada e depois dispara. Ajuste um de cada vez, com o jogo rodando, e observe no Game view com o Gizmo da composição ligado.

Esse tipo de ajuste é parte do que se chama de game feel. Se você quer entender por que meio segundo de damping muda a sensação do jogo inteiro, o post sobre game feel e juice mostra o quadro completo.

Confiner 2D: limites do mapa

Sem limite, a câmera mostra o fundo azul do editor quando o jogador chega na borda da fase. O Cinemachine Confiner 2D resolve isso.

Passo a passo:

  1. Crie um GameObject vazio chamado CameraBounds e adicione um PolygonCollider2D. Edite os pontos para contornar a área que a câmera pode mostrar. Marque Is Trigger, para o jogador não colidir com ele.
  2. Coloque esse objeto numa layer que não colida com nada (por exemplo, uma layer CameraBounds desmarcada na matriz de colisão em Project Settings > Physics 2D).
  3. Selecione a CinemachineCamera, clique em Add Extension e escolha CinemachineConfiner2D.
  4. Arraste o CameraBounds para Bounding Shape 2D.

As outras propriedades: Damping suaviza a câmera nos cantos da forma, Slowing Distance cria uma zona de frenagem perto da borda e Max Window Size e Padding ajudam quando a área visível é maior que a forma.

Para mapas montados por partes, a documentação sugere um CompositeCollider2D num objeto pai (Is Trigger ligado, Geometry Type em Polygons e Rigidbody2D Static) com os colliders das partes nos filhos usando Composite Operation em Merge.

O confiner guarda um cache da forma. Se o collider muda durante o jogo (porta que abre e aumenta a área, sala que se expande), chame InvalidateBoundingShapeCache(). Se o Orthographic Size muda (zoom), chame InvalidateLensCache(). O método antigo InvalidateCache() está marcado como obsoleto no Cinemachine 3.

Arquivo CameraBoundsUpdater.cs:

using UnityEngine;
using Unity.Cinemachine;

public class CameraBoundsUpdater : MonoBehaviour
{
    [SerializeField] private CinemachineConfiner2D confiner;
    [SerializeField] private CinemachineCamera cinemachineCamera;

    public void SetBounds(Collider2D newBounds)
    {
        confiner.BoundingShape2D = newBounds;
        confiner.InvalidateBoundingShapeCache();
    }

    public void SetZoom(float orthographicSize)
    {
        cinemachineCamera.Lens.OrthographicSize = orthographicSize;
        confiner.InvalidateLensCache();
    }
}

Lens é uma struct (LensSettings), mas a CinemachineCamera expõe o campo diretamente, então a atribuição em Lens.OrthographicSize funciona.

Trocar de sala com várias câmeras

Em metroidvania e em jogo de sala única (estilo Celeste), cada sala tem a sua câmera e o seu confiner. A troca é feita pela prioridade: o Cinemachine Brain sempre mostra a CinemachineCamera ativa de maior prioridade, e faz um blend entre elas.

Monte assim: para cada sala, um objeto com BoxCollider2D (Is Trigger) cobrindo a sala, e dentro dele uma CinemachineCamera com o seu Confiner 2D. Todas com prioridade 0. O script sobe a prioridade da câmera da sala em que o jogador entrou.

Arquivo RoomCamera.cs:

using UnityEngine;
using Unity.Cinemachine;

[RequireComponent(typeof(Collider2D))]
public class RoomCamera : MonoBehaviour
{
    [SerializeField] private CinemachineCamera roomCamera;
    [SerializeField] private string playerTag = "Player";
    [SerializeField] private int activePriority = 10;
    [SerializeField] private int inactivePriority = 0;

    private void Awake()
    {
        roomCamera.Priority.Value = inactivePriority;
    }

    private void OnTriggerEnter2D(Collider2D other)
    {
        if (other.CompareTag(playerTag))
        {
            roomCamera.Priority.Value = activePriority;
        }
    }

    private void OnTriggerExit2D(Collider2D other)
    {
        if (other.CompareTag(playerTag))
        {
            roomCamera.Priority.Value = inactivePriority;
        }
    }
}

No Cinemachine 3, Priority é do tipo PrioritySettings, com a propriedade Value. O jogador precisa ter Rigidbody2D e Collider2D para o trigger disparar. O tempo e o estilo da transição ficam no Cinemachine Brain, em Default Blend: um Ease In Out de 0.4 segundo é o padrão confortável, e Cut dá a troca seca de jogo retrô.

Juntar câmera, sala, transição e personagem num jogo que funcione do começo ao fim é o ponto em que muita gente empaca sozinha. Cada peça isolada tem tutorial; o que falta é ver o projeto inteiro montado em ordem, com as decisões explicadas, e é para isso que existe um curso de Unity estruturado.

Curso de Unity
Quer fazer seu primeiro jogo na Unity?

No curso de Unity 6 da CursoGame.Dev você vai do zero ao primeiro jogo, com um desafio prático em cada level e suporte individual no WhatsApp quando travar.

Conhecer o curso de Unity
Suporte no WhatsAppAcesso vitalícioGarantia de 7 dias

Pixel perfect com Cinemachine

Se o seu jogo é pixel art, a Main Camera vai ter o componente Pixel Perfect Camera do URP. O problema: o Pixel Perfect Camera e o Cinemachine brigam pelo Orthographic Size. A solução oficial é a extensão Cinemachine Pixel Perfect:

  1. Configure o Pixel Perfect Camera na Main Camera (Assets Pixels Per Unit igual ao PPU dos sprites e a Reference Resolution do jogo).
  2. Em cada CinemachineCamera, Add Extension > CinemachinePixelPerfect.

A extensão lê as configurações do Pixel Perfect Camera e ajusta o Orthographic Size de cada câmera virtual para o valor pixel perfect mais próximo do enquadramento que você definiu. Duas limitações documentadas: durante o blend entre câmeras a imagem não fica pixel perfect (fica de novo quando a transição termina), e com Upscale Render Texture ligado o número de resoluções possíveis cai, e o enquadramento pode ficar visivelmente diferente do que você ajustou. Os detalhes de PPU e resolução de referência estão no post de Pixel Perfect Camera na Unity.

Erros comuns: tremedeira e confiner que não funciona

O personagem treme, mas o cenário não. O jogador se move na física (FixedUpdate) e a câmera atualiza no frame. Ligue Interpolate no Rigidbody2D do jogador. Se ainda tremer, no Cinemachine Brain teste Update Method em Smart Update ou Late Update, que são os modos indicados para alvo com interpolação.

O cenário inteiro treme em pixel art. Falta a extensão Cinemachine Pixel Perfect, ou o Damping está tão baixo que a câmera para em posições fracionárias a cada frame. Com Grid Snapping em Pixel Snapping no Pixel Perfect Camera, os sprites encaixam no grid.

O confiner é ignorado. Confira, nesta ordem: a extensão está na CinemachineCamera (não na Main Camera); o collider é PolygonCollider2D, BoxCollider2D ou CompositeCollider2D com Geometry Type em Polygons; a forma é maior que a área visível (se a sala for menor que a tela, a câmera não tem para onde ir e fica centralizada); você chamou InvalidateBoundingShapeCache() depois de mudar o collider.

O jogador colide com a borda invisível. O collider do confiner está sem Is Trigger ou na mesma layer do jogador. Ajuste a layer na matriz de colisão.

A câmera dá um tranco ao entrar na sala. O blend está longo demais ou o Center On Activate do Position Composer está desligado. Ligado, ele centraliza o alvo na dead zone quando a câmera assume.

Próximo passo

Monte uma fase de teste com três salas de tamanhos diferentes, uma câmera por sala e um corredor estreito entre elas. Ajuste dead zone, damping e lookahead até o pulo não balançar a tela e a corrida mostrar o buraco antes da queda. Depois, se o jogo for pixel art, siga para o guia de Pixel Perfect Camera e trave a nitidez. Se em algum momento você quiser comparar com outra engine, o mesmo sistema na Godot está em câmera 2D que segue o jogador.

Perguntas frequentes

Qual a diferença entre o Cinemachine 2 e o Cinemachine 3 para jogos 2D?

No Cinemachine 3 a CinemachineVirtualCamera virou CinemachineCamera, o Framing Transposer virou Position Composer e o namespace passou a ser Unity.Cinemachine. A lógica de dead zone, damping e confiner continua parecida, mas tutoriais antigos usam nomes de componente e de propriedade que não existem mais.

Por que o Cinemachine Confiner 2D não funciona?

Os motivos mais comuns são collider que não é PolygonCollider2D, BoxCollider2D ou CompositeCollider2D com Geometry Type em Polygons, collider marcado sem Is Trigger e colidindo com o jogador, ou forma alterada em tempo de jogo sem chamar InvalidateBoundingShapeCache. Também confira se a extensão foi adicionada na CinemachineCamera e não na Main Camera.

Preciso de script para a câmera seguir o jogador com Cinemachine?

Não. Basta arrastar o jogador para o campo Tracking Target da CinemachineCamera. Script só entra para casos como trocar de câmera por sala, mudar o zoom em tempo de jogo ou recalcular o confiner quando o mapa muda.

Como deixar a câmera do Cinemachine mais solta ou mais presa ao jogador?

Aumente a Dead Zone para o jogador andar sem arrastar a câmera e aumente o Damping para a câmera chegar mais devagar. Valores de Damping perto de zero deixam a câmera colada, o que costuma cansar a vista em jogo de plataforma.