Voltar para o Blog
Quest Log

Camera Shake na Unity: Impulse do Cinemachine e por Script

Cena de jogo com uma explosão e o quadro da câmera deslocado em várias posições sobrepostas, sugerindo tremor

Unity camera shake de dois jeitos: Cinemachine Impulse Source e Listener disparados por evento, e script com Perlin noise e trauma, com opção de desligar.

Ferramenta:UnityC#

Camera Shake na Unity: Impulse do Cinemachine e por Script

Existem dois caminhos para fazer unity camera shake: se o projeto usa Cinemachine, você coloca um Impulse Source no objeto que causa o tremor (a explosão, o golpe) e um Impulse Listener na câmera, e dispara o impulso por código. Se o projeto não usa Cinemachine, um script de 50 linhas com Perlin noise e um valor de trauma que decai dá um tremor mais controlável que qualquer Random.Range.

Este guia monta os dois, com intensidade diferente por evento, a opção de desligar o tremor nas configurações (acessibilidade) e a correção do erro mais comum: a câmera que não volta para o lugar depois de tremer. Todo o código é para o Unity 6.3 LTS com Cinemachine 3.

Unity camera shake: quando usar

Tremor de tela comunica impacto. Ele diz ao jogador que algo pesado aconteceu, e por isso funciona em explosão, dano recebido, aterrissagem de queda alta, golpe de chefe e disparo de arma pesada. É uma das ferramentas clássicas de game feel, ao lado de hit stop, partículas e som. O post de game feel e juice mostra como essas peças trabalham juntas.

Ele deixa de funcionar quando vira ruído de fundo. Três regras práticas:

  • Tremor proporcional ao evento. Tiro de pistola treme quase nada; explosão de chefe treme muito. Se tudo treme igual, nada parece forte.
  • Curto. A maioria dos tremores bons dura entre 0.1 e 0.4 segundo.
  • Nunca em ação contínua. Metralhadora disparando 10 vezes por segundo com shake em cada tiro deixa a tela ilegível.

Cinemachine Impulse: Source e Listener

Quando a câmera é controlada pelo Cinemachine, o Cinemachine Brain reescreve a posição da Main Camera todo frame. Qualquer script que mexa no Transform dela é sobrescrito. O tremor precisa acontecer dentro do Cinemachine, e o sistema feito para isso é o Impulse.

Ele tem duas pontas:

  • Cinemachine Impulse Source: componente no objeto que emite o tremor. Ele define a forma do sinal e a força.
  • Cinemachine Impulse Listener: extensão na CinemachineCamera que recebe o sinal e treme a câmera.

Montando o Listener:

  1. Selecione a CinemachineCamera.
  2. No Inspector, em Add Extension, escolha CinemachineImpulseListener.
  3. Deixe Gain em 1 por enquanto. Em jogo 2D, marque Use 2D Distance, para a distância até a fonte ignorar o eixo Z.

Montando o Source:

  1. No prefab da explosão (ou no inimigo, ou no jogador), Add Component > Cinemachine Impulse Source.
  2. Em Predefined Impulse Shape, escolha uma forma: Recoil, Bump, Explosion ou Rumble. Para explosão, comece por Explosion; para dano no jogador, Bump.
  3. Ajuste a duração no campo de segundos ao lado da forma.
  4. Default Velocity é a direção e a força padrão do impulso. Para 2D, algo como (0, -0.3, 0) treme para baixo e volta.
  5. Impulse Type define como o sinal se espalha. Uniform atinge todos os listeners com a mesma força. Dissipating perde força com a distância (use Dissipation Distance). Propagating também tem atraso de propagação, como uma onda.

Para testar sem código, use o botão Test with Force do Source em Play Mode.

Os canais (Impulse Channel no Source e Channel Mask no Listener) servem para separar sinais. Por exemplo: um canal para explosões do cenário e outro para dano no jogador, e uma câmera de minimapa que só escuta o primeiro.

Disparar impulso por código

No Cinemachine 3, os métodos do Impulse Source são GenerateImpulse(), GenerateImpulseWithForce(float force), GenerateImpulseWithVelocity(Vector3 velocity) e GenerateImpulseAtPositionWithVelocity(Vector3 position, Vector3 velocity). As versões antigas com sobrecarga (GenerateImpulse(float) e GenerateImpulseAt) estão marcadas como legado.

Arquivo ExplosionShake.cs, no prefab da explosão:

using UnityEngine;
using Unity.Cinemachine;

[RequireComponent(typeof(CinemachineImpulseSource))]
public class ExplosionShake : MonoBehaviour
{
    [SerializeField] private float baseForce = 1f;

    private CinemachineImpulseSource impulseSource;

    private void Awake()
    {
        impulseSource = GetComponent<CinemachineImpulseSource>();
    }

    private void Start()
    {
        float force = baseForce * ShakeSettings.Intensity;
        if (force > 0f)
        {
            impulseSource.GenerateImpulseWithForce(force);
        }
    }
}

Aqui a explosão treme ao nascer. Num inimigo, chame o método no momento do golpe. O ShakeSettings.Intensity é o controle de acessibilidade, montado mais abaixo.

Para intensidade por evento, crie Sources com forças diferentes (granada com baseForce 0.5, míssil com 1.5) ou passe a força calculada na hora, por exemplo proporcional ao dano recebido.

Para impacto físico, como uma caixa pesada caindo no chão ou um chefe aterrissando, o Cinemachine também tem o Cinemachine Collision Impulse Source. Ele dispara o impulso sozinho quando o objeto colide ou entra num trigger, filtrando por layer, sem nenhuma linha de código. Use para o que acontece por física e deixe o disparo manual para o que acontece por regra de jogo (dano, morte, habilidade).

Uma decisão que economiza retrabalho: centralize o disparo. Em vez de cada inimigo ter a sua lógica de força, crie um script único com métodos como ShakeLight(), ShakeHeavy() e ShakeExplosion(), cada um com o seu valor. Quando o tremor estiver forte demais no playtest, você ajusta em um lugar só.

Shake por script com Perlin noise

Sem Cinemachine, o tremor é seu. A versão ingênua soma um Random.insideUnitCircle na posição da câmera a cada frame. Funciona, mas é áspero: cada frame sorteia um ponto sem relação com o anterior, e a tela parece um vídeo com defeito.

O Mathf.PerlinNoise(float x, float y) devolve um valor entre 0 e 1 (pode passar levemente desses limites) que varia de forma contínua. Percorrendo o ruído com o tempo, você tem um tremor que oscila forte sem pular. Para ter valores entre -1 e 1, multiplique por 2 e subtraia 1. Usando uma semente diferente em cada eixo (X, Y e rotação), os três não andam juntos.

Um detalhe de arquitetura que evita a maioria dos bugs: o tremor fica num objeto filho. A câmera de seguir (o seu script de follow) move o objeto pai; o shake mexe só na localPosition e na localRotation do filho, onde está o componente Camera. Assim, um não sobrescreve o outro.

CameraRig      (script de seguir o jogador)
└── Main Camera (Camera + CameraShake)

Trauma: intensidade que decai

A ideia de trauma ficou popular com a palestra de Squirrel Eiserloh na GDC 2016 sobre câmeras. Em vez de "tremer X por Y segundos", cada impacto soma trauma (um valor de 0 a 1), o trauma cai com o tempo, e o tremor é proporcional ao trauma ao quadrado. Com o quadrado, trauma 0.3 treme 0.09 e trauma 1 treme 1: impactos pequenos ficam discretos, grandes ficam fortes, e vários impactos seguidos acumulam sem reiniciar o tremor.

Arquivo CameraShake.cs, no objeto filho que tem a Camera:

using UnityEngine;

public class CameraShake : MonoBehaviour
{
    [SerializeField] private float maxOffset = 0.5f;
    [SerializeField] private float maxAngle = 3f;
    [SerializeField] private float frequency = 25f;
    [SerializeField] private float traumaDecayPerSecond = 1.5f;

    private float trauma = 0f;
    private float seed = 0f;

    private void Awake()
    {
        seed = Random.Range(0f, 1000f);
    }

    public void AddTrauma(float amount)
    {
        trauma = Mathf.Clamp01(trauma + amount * ShakeSettings.Intensity);
    }

    private void LateUpdate()
    {
        if (trauma <= 0f)
        {
            transform.localPosition = Vector3.zero;
            transform.localRotation = Quaternion.identity;
            return;
        }

        float shake = trauma * trauma;
        float time = Time.time * frequency;

        float offsetX = maxOffset * shake * Noise(seed, time);
        float offsetY = maxOffset * shake * Noise(seed + 1f, time);
        float angle = maxAngle * shake * Noise(seed + 2f, time);

        transform.localPosition = new Vector3(offsetX, offsetY, 0f);
        transform.localRotation = Quaternion.Euler(0f, 0f, angle);

        trauma = Mathf.Max(0f, trauma - traumaDecayPerSecond * Time.deltaTime);
    }

    private float Noise(float noiseSeed, float time)
    {
        return Mathf.PerlinNoise(noiseSeed, time) * 2f - 1f;
    }
}

Para disparar, de qualquer script que tenha a referência:

cameraShake.AddTrauma(0.3f); // dano leve
cameraShake.AddTrauma(0.8f); // explosão perto

Ajuste fino: frequency alto (25 a 40) dá tremor nervoso de impacto; baixo (5 a 10) dá balanço de terremoto. Em pixel art, maxAngle em 0 evita sprites girados com pixel serrilhado.

O shake pronto é a parte fácil. O que dá trabalho é ligar dano, morte, explosão, som e câmera num jogo que tenha ritmo, sem cada sistema virar um remendo no outro. Esse é o salto entre testar efeitos soltos e terminar um jogo, e é o que um curso de Unity com projeto completo ensina na ordem certa.

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

Opção de desligar o tremor

Tremor de tela incomoda parte dos jogadores, e alguns sentem enjoo. Uma opção de intensidade no menu custa pouco. Arquivo ShakeSettings.cs:

using UnityEngine;

public static class ShakeSettings
{
    private const string PrefKey = "screen_shake_intensity";

    public static float Intensity
    {
        get { return PlayerPrefs.GetFloat(PrefKey, 1f); }
        set
        {
            PlayerPrefs.SetFloat(PrefKey, Mathf.Clamp01(value));
            PlayerPrefs.Save();
        }
    }
}

No menu de opções, ligue um Slider de 0 a 1 ao ShakeSettings.Intensity. Os dois sistemas deste post já multiplicam a força por ele. No Cinemachine, outra opção é ajustar o Gain do Impulse Listener pela mesma configuração.

Se o seu jogo também usa ruído contínuo de câmera (o componente Basic Multi Channel Perlin do Cinemachine, que dá aquela respiração de câmera na mão), aplique a configuração no AmplitudeGain dele:

using UnityEngine;
using Unity.Cinemachine;

public class HandheldNoiseSetting : MonoBehaviour
{
    [SerializeField] private CinemachineBasicMultiChannelPerlin noise;
    [SerializeField] private float baseAmplitude = 0.5f;

    private void Start()
    {
        noise.AmplitudeGain = baseAmplitude * ShakeSettings.Intensity;
    }
}

Erros comuns: câmera que não volta ao lugar

A câmera termina o tremor deslocada. O shake está somando offset na posição em vez de definir a posição. Se cada frame faz transform.position += offset, os deslocamentos acumulam. A correção é a do script acima: o shake fica num filho e define a localPosition a partir de zero, e zera tudo quando o trauma acaba.

O shake briga com o script de seguir. Os dois mexem no mesmo Transform. Separe em pai (seguir) e filho (tremer).

Nada treme com Cinemachine. Faltou o Impulse Listener na CinemachineCamera, o Channel Mask do Listener não inclui o canal do Source, ou o Impulse Type é Dissipating e a fonte está longe demais. Teste com Uniform para isolar o problema.

Tremor fraco em 2D com Impulse. Sem Use 2D Distance, a distância em Z entre câmera e fonte (10 unidades, em geral) entra na conta de dissipação.

Shake depende do frame rate. Um decaimento sem Time.deltaTime dura menos em monitor de 144 Hz. O script acima já multiplica.

Próximo passo

Escolha três eventos do seu jogo (dano leve, dano forte, explosão) e dê a cada um um valor de trauma diferente. Jogue com o slider de intensidade em 100, 50 e 0 e veja se o jogo continua legível em todos. Para comparar implementações, o mesmo sistema na Godot está em camera shake na Godot, e o conjunto de câmeras com Cinemachine no Unity está no guia de Cinemachine.

Perguntas frequentes

Por que meu camera shake não funciona com Cinemachine?

Quando existe um Cinemachine Brain na Main Camera, ele reescreve a posição da câmera a cada frame, então mexer no Transform da Main Camera não aparece na tela. Use o Impulse (Source mais Listener na CinemachineCamera) ou o componente Basic Multi Channel Perlin, que tremem a câmera virtual por dentro do Cinemachine.

O que é trauma no screen shake?

Trauma é um valor de 0 a 1 que sobe com cada impacto e cai com o tempo. A intensidade do tremor é o trauma elevado ao quadrado ou ao cubo, o que faz impactos pequenos tremerem pouco e impactos grandes tremerem bem mais. Vários eventos somam trauma em vez de reiniciar o tremor.

Por que usar Perlin noise e não Random.Range no camera shake?

Random.Range sorteia um valor sem relação com o anterior, e a câmera pula de um ponto a outro de forma áspera. O Perlin noise varia de forma contínua, então o tremor fica forte sem parecer defeito de vídeo, e você controla a frequência pela velocidade com que percorre o ruído.

Preciso dar opção de desligar o tremor de tela?

É uma boa prática de acessibilidade. Tremor de tela causa enjoo e desconforto em parte dos jogadores. Um controle de intensidade de 0 a 100 por cento no menu de opções, multiplicando toda a força do shake, resolve com poucas linhas.