Camera shake na Godot: tremor de tela com código

Aprenda a fazer camera shake na Godot 4 com GDScript: tremor de tela com offset aleatório, decaimento, controle de intensidade e noise para impactos.
Um bom camera shake na Godot transforma um impacto sem graça num golpe que o jogador sente no corpo. O tremor de tela é um daqueles detalhes de feedback que ninguém nota quando está bem feito, mas que deixa tudo plano quando falta: o tiro que acerta, a explosão que estoura, o pulo que aterrissa pesado. Neste guia você vai montar um sistema de tremor de tela na Godot 4 com GDScript tipado, começando por um shake simples com offset aleatório e evoluindo para uma versão com noise e controle de intensidade que dá para disparar de qualquer lugar do jogo.
Como funciona o camera shake na Godot
A ideia por trás do tremor de tela é simples: deslocar o enquadramento da câmera por pequenos valores aleatórios durante alguns instantes e depois voltar ao normal. O ponto importante é onde aplicar esse deslocamento.
Na Camera2D existe a propriedade offset, um Vector2 que move só o que a câmera mostra, sem tocar na posição real do nó. Isso é exatamente o que queremos. Se você somar o tremor na global_position, o shake vai brigar com a lógica que faz a câmera seguir o jogador e a câmera acaba derivando do lugar. Usando o offset, o seguimento continua funcionando normalmente enquanto o tremor acontece por cima.
Se a sua câmera ainda não acompanha o personagem, vale montar essa base primeiro no guia de câmera 2D que segue o jogador, porque o shake entra como uma camada em cima desse seguimento.
A estrutura mínima de qualquer shake tem três partes: uma intensidade atual, um deslocamento aleatório por quadro e um decaimento que leva a intensidade de volta a zero. Vamos construir cada uma.
Tremor de tela simples com offset aleatório
Comece com a versão mais direta. Crie um script na sua Camera2D com uma variável de intensidade e um temporizador de duração. A cada quadro, se ainda houver tempo, sorteie um offset dentro de um raio proporcional à intensidade:
extends Camera2D
@export var intensidade_max: float = 12.0
@export var duracao: float = 0.3
var tempo_restante: float = 0.0
func _process(delta: float) -> void:
if tempo_restante > 0.0:
tempo_restante -= delta
var forca: float = intensidade_max * (tempo_restante / duracao)
offset = Vector2(
randf_range(-forca, forca),
randf_range(-forca, forca)
)
else:
offset = Vector2.ZERO
func tremer() -> void:
tempo_restante = duracao
Repare em dois detalhes. Primeiro, tudo é tipado: intensidade_max: float, o retorno -> void, as variáveis locais com : float. No Godot 4 essa é a forma recomendada de escrever GDScript, porque o editor pega erros de tipo antes de rodar e o código fica um pouco mais rápido.
Segundo, a forca diminui sozinha conforme o tempo_restante cai, porque multiplicamos a intensidade pela fração tempo_restante / duracao. Isso cria um decaimento linear: o tremor começa forte e some suave até zerar. Quando o tempo acaba, o offset volta para Vector2.ZERO e a câmera fica estável de novo.
Para disparar, basta chamar tremer() na câmera no momento do impacto. Esse modelo já resolve a maioria dos casos de um protótipo.
Decaimento com trauma elevado ao quadrado
O decaimento linear funciona, mas tem um problema: shakes seguidos não se acumulam bem e o controle de intensidade fica grosseiro. Uma abordagem mais robusta, popular na comunidade, usa o conceito de trauma, um valor de 0 a 1 que você eleva ao quadrado (ou ao cubo) antes de aplicar.
Elevar ao quadrado faz o tremor cair de forma mais natural: começa intenso e perde força rápido, como um impacto real. E como o trauma é somado e limitado entre 0 e 1, vários impactos próximos se empilham sem estourar:
extends Camera2D
@export var intensidade_max: float = 20.0
@export var decaimento: float = 1.5
var trauma: float = 0.0
func _process(delta: float) -> void:
if trauma > 0.0:
trauma = maxf(trauma - decaimento * delta, 0.0)
var forca: float = intensidade_max * (trauma * trauma)
offset = Vector2(
randf_range(-forca, forca),
randf_range(-forca, forca)
)
else:
offset = Vector2.ZERO
func adicionar_trauma(valor: float) -> void:
trauma = minf(trauma + valor, 1.0)
Agora, em vez de reiniciar o tremor, você soma trauma com adicionar_trauma(0.4). Um impacto pequeno manda 0.2, uma explosão manda 0.8. Se dois acertos vierem juntos, o trauma sobe até o teto de 1.0 e o tremor fica intenso sem bugar. O decaimento * delta garante que o shake dure o mesmo tempo independente do FPS, resolvendo o erro clássico de subtrair um valor fixo por quadro.
Tremor suave com FastNoiseLite
O tremor com randf_range é nervoso e cheio de saltos, ótimo para golpes secos. Mas para rajadas longas, um terremoto ou o tranco de um motor, um tremor mais contínuo fica melhor. É aí que entra o FastNoiseLite, que gera uma curva suave onde quadros vizinhos têm valores próximos.
A diferença prática é que, em vez de sortear um ponto solto por quadro, você avança no tempo dentro do noise e lê valores em sequência, criando um movimento fluido. Use dois canais de noise com seeds diferentes para X e Y não ficarem iguais:
extends Camera2D
@export var intensidade_max: float = 20.0
@export var decaimento: float = 1.5
@export var velocidade_noise: float = 50.0
var trauma: float = 0.0
var tempo_noise: float = 0.0
var ruido_x: FastNoiseLite = FastNoiseLite.new()
var ruido_y: FastNoiseLite = FastNoiseLite.new()
func _ready() -> void:
ruido_x.seed = 1
ruido_y.seed = 2
ruido_x.frequency = 0.5
ruido_y.frequency = 0.5
func _process(delta: float) -> void:
if trauma > 0.0:
trauma = maxf(trauma - decaimento * delta, 0.0)
tempo_noise += delta * velocidade_noise
var forca: float = intensidade_max * (trauma * trauma)
offset = Vector2(
ruido_x.get_noise_1d(tempo_noise) * forca,
ruido_y.get_noise_1d(tempo_noise) * forca
)
else:
offset = Vector2.ZERO
func adicionar_trauma(valor: float) -> void:
trauma = minf(trauma + valor, 1.0)
O get_noise_1d devolve um valor entre -1 e 1 que varia suavemente conforme tempo_noise cresce. Multiplicando pela forca, você tem um tremor orgânico que ainda respeita o decaimento do trauma. Ajuste velocidade_noise para deixar o tremor mais lento e pesado ou mais rápido e vibrante, e a frequency do noise para mudar a textura do movimento.
Se quiser somar polimento, dá para animar uma rotação leve junto do offset usando um tween por código, por exemplo um pequeno giro que volta ao zero no fim do impacto. O cuidado é não exagerar: tremor de tela em excesso cansa o jogador e atrapalha a leitura da ação.
Disparando o shake de qualquer lugar
Até aqui o shake vive na própria câmera. O problema é que armas, inimigos e o sistema de dano raramente têm uma referência direta a ela. A solução limpa é expor um método reutilizável e chamá-lo por grupo ou por um autoload.
A forma mais simples sem criar um singleton é colocar a câmera num grupo e disparar o trauma por ele:
# na câmera, dentro do _ready()
add_to_group("camera_principal")
# de qualquer outro script, no momento do impacto
func aplicar_impacto() -> void:
var cameras: Array = get_tree().get_nodes_in_group("camera_principal")
for cam in cameras:
if cam.has_method("adicionar_trauma"):
cam.adicionar_trauma(0.5)
Assim o código do inimigo ou da arma não precisa saber onde a câmera está na árvore. Ele só pede o tremor pelo grupo e segue a vida. Se o seu projeto cresce, vale centralizar isso num autoload com um sinal shake_requested(forca) que a câmera escuta, mantendo o acoplamento baixo.
Esse mesmo raciocínio de deslocamento com decaimento vale em 3D. A Camera3D não tem offset 2D, então você aplica um Vector3 de tremor na position local da câmera ou, melhor, num nó pai dedicado ao shake, para não brigar com o seguimento do personagem. Se você está começando no 3D, o passo a passo de primeiro jogo 3D na Godot monta a cena e a câmera onde esse tremor vai entrar.
Ajustando o tremor para cada situação
Tremor de tela é tempero, e o erro mais comum é usar a mesma intensidade para tudo. Vale calibrar por tipo de evento:
- Passos pesados ou dano leve: trauma baixo, algo como
0.15, quase imperceptível. - Tiro ou acerto de espada: trauma médio, perto de
0.4. - Explosão ou morte de chefe: trauma alto,
0.7ou mais, com duração maior.
Outra dica é oferecer uma opção de desligar ou reduzir o shake nas configurações. Parte dos jogadores sente enjoo com tremor forte, e deixar isso ajustável é acessibilidade básica. Guarde um multiplicador global de intensidade e aplique na forca antes de mandar para o offset.
Por fim, teste o tremor junto com o resto do feedback, não isolado. Um impacto convincente combina shake com um efeito sonoro, uma pausa curta de tempo (o famoso hit stop) e talvez uma partícula. O tremor sozinho chama atenção para si mesmo; dentro de um conjunto, ele some e só deixa a sensação de peso.
Conclusão
Fazer camera shake na Godot 4 começa com três ingredientes: um offset aleatório, uma intensidade e um decaimento. A partir daí você escolhe o sabor. O randf_range dá um tremor seco para impactos pontuais, o trauma elevado ao quadrado faz o shake cair de forma natural e acumular bem, e o FastNoiseLite entrega um movimento suave para eventos longos. Com um método como adicionar_trauma exposto por grupo ou autoload, qualquer parte do jogo dispara o tremor sem conhecer a câmera. Calibre a intensidade por evento, dê uma opção de reduzir nas configurações e use o shake como parte de um conjunto de feedback, nunca sozinho. Se você está decidindo o caminho para aprender tudo isso de forma estruturada, o texto sobre aprender a criar jogos sozinho ou com curso ajuda a pesar as opções.
Perguntas frequentes
Devo aplicar o camera shake no offset ou na position da câmera?
Use o offset. A propriedade offset da Camera2D desloca só o enquadramento sem mexer na posição real do nó, então o seguimento do player continua intacto. Se você somar o tremor na global_position, o shake briga com a lógica de seguir o alvo e a câmera tende a derivar do lugar.
Qual a diferença entre tremor com random e com noise na Godot?
O random sorteia um valor novo a cada quadro, gerando um tremor nervoso e cheio de saltos, bom para impactos secos. O FastNoiseLite lê uma curva contínua, então os quadros vizinhos ficam próximos e o tremor sai mais suave e orgânico, melhor para rajadas longas ou tremores de fundo.
Como fazer camera shake em 3D na Godot?
A lógica é a mesma, mas a Camera3D não tem offset 2D. Você aplica o deslocamento na position local da câmera ou num nó pai dela, usando um Vector3 de tremor, e zera no fim. Manter o shake num nó pai separado evita conflito com o seguimento do personagem no mundo 3D.
Por que meu tremor de tela fica dependente do FPS?
Quase sempre é porque o decaimento foi feito subtraindo um valor fixo por quadro em vez de multiplicar pelo delta. Reduza a intensidade usando trauma -= decaimento * delta ou um lerp com delta, assim o shake dura o mesmo tempo em 30 ou 144 quadros por segundo.


