Como Criar uma Tela de Game Over no Godot 4

Aprenda a montar uma tela game over godot com CanvasLayer, sinal de morte, fade com Tween e botoes para reiniciar ou voltar ao menu principal.
Quase todo jogo precisa de um momento que diz "voce perdeu, e agora?". Montar uma tela game over godot bem feita parece simples, mas envolve mais coisa do que so mostrar um texto: voce precisa pausar a acao, escutar o momento certo da morte do jogador, mostrar a interface por cima de tudo e dar opcoes claras para o jogador continuar. Neste tutorial vamos resolver isso de um jeito limpo, usando recursos nativos do Godot 4.
Como Criar uma Tela de Game Over no Godot 4
A ideia aqui nao e decorar passos, e entender como as pecas se encaixam. Vamos usar um CanvasLayer para garantir que a tela apareca acima do jogo, um sinal para avisar quando o jogador morre, um Tween para o fade de entrada e dois botoes: reiniciar e voltar ao menu. No fim, voce vai ter uma estrutura que pode reaproveitar em qualquer projeto.
Por que usar um CanvasLayer
O primeiro erro comum de quem esta comecando e colocar a tela de game over como um filho qualquer da cena. O problema: se a sua cena tem uma camera que se move, ou nodes com z_index variado, a interface pode ficar atras de coisas ou se mexer junto com o mundo. O CanvasLayer resolve isso porque ele desenha em uma camada separada, fixa na tela, ignorando a camera do jogo.
Crie uma cena nova com essa estrutura:
GameOver (CanvasLayer)
└── ColorRect (fundo escuro semitransparente)
└── CenterContainer
└── VBoxContainer
├── Label ("Game Over")
├── Button (Reiniciar)
└── Button (Menu)
O ColorRect cobre a tela inteira e serve tanto de fundo escuro quanto de alvo para o fade. Deixe a cor com um alpha baixo no editor (algo como preto com 70% de opacidade) para escurecer o jogo sem esconder ele por completo. O CenterContainer centraliza o conteudo independente da resolucao, o que evita dor de cabeca com telas de tamanhos diferentes.
O sinal de morte do jogador
A tela de game over nao deve adivinhar quando aparecer. Quem sabe que o jogador morreu e o proprio jogador (ou o sistema de vida dele). Por isso usamos um sinal. O node do player emite um aviso, e quem estiver interessado escuta. Isso mantem o player desacoplado da interface: ele nao precisa saber que existe uma tela de game over, so anuncia que morreu.
No script do jogador:
extends CharacterBody2D
signal morreu
@export var vida_maxima: int = 3
var vida: int
func _ready() -> void:
vida = vida_maxima
func receber_dano(quantidade: int) -> void:
vida -= quantidade
if vida <= 0:
morrer()
func morrer() -> void:
# evita emitir o sinal duas vezes se levar dano de novo
set_physics_process(false)
emit_signal("morreu")
Repare que desligamos o _physics_process ao morrer. Isso impede que o player continue se movendo ou levando dano depois que ja deveria estar morto, um bug chato que aparece quando dois inimigos acertam o jogador no mesmo frame.
Conectando o sinal e instanciando a tela
Agora alguem precisa escutar o sinal morreu. O lugar natural e a cena principal do nivel, que conhece tanto o player quanto a hora de mostrar a interface. Vamos conectar o sinal e instanciar a cena de game over apenas quando ela for necessaria, em vez de deixar ela carregada o tempo todo.
extends Node2D
@export var cena_game_over: PackedScene
@onready var player: CharacterBody2D = $Player
func _ready() -> void:
player.morreu.connect(_on_player_morreu)
func _on_player_morreu() -> void:
var tela := cena_game_over.instantiate()
add_child(tela)
No Godot 4, sinais sao objetos de primeira classe, entao player.morreu.connect(...) e a forma recomendada, mais segura que passar o nome do sinal como string. Arraste a cena GameOver.tscn para o campo cena_game_over no Inspetor e o nivel ja sabe o que mostrar quando o player cair.
Pausando o jogo do jeito certo
Quando a tela de game over aparece, o jogo precisa parar. Inimigos nao devem continuar andando, projeteis nao devem voar. O Godot tem o get_tree().paused, mas tem um detalhe: se voce pausar tudo, a propria tela de game over para de funcionar tambem, incluindo a animacao de fade e os cliques nos botoes.
A solucao e o process_mode. No node raiz da cena GameOver, defina o process_mode como PROCESS_MODE_ALWAYS no Inspetor (ou via codigo). Assim, mesmo com a arvore pausada, a tela continua respondendo.
extends CanvasLayer
@onready var fundo: ColorRect = $ColorRect
@onready var botao_reiniciar: Button = $ColorRect/CenterContainer/VBoxContainer/Reiniciar
@onready var botao_menu: Button = $ColorRect/CenterContainer/VBoxContainer/Menu
func _ready() -> void:
process_mode = Node.PROCESS_MODE_ALWAYS
get_tree().paused = true
_fade_in()
botao_reiniciar.pressed.connect(_on_reiniciar)
botao_menu.pressed.connect(_on_menu)
Pausar a arvore aqui dentro da propria tela, no _ready, garante que o jogo congela no exato instante em que a interface surge. Se voce ja trabalha com um menu de pause, vai notar que a logica do process_mode e a mesma: a UI que precisa funcionar durante a pausa fica em modo "always".
O fade de entrada com Tween
Aparecer de forma brusca passa a sensacao de bug. Um fade rapido (algo entre 0.3 e 0.6 segundos) deixa a transicao suave e da um respiro para o jogador entender o que aconteceu. No Godot 4, criamos um Tween em tempo de execucao com create_tween() e animamos a propriedade modulate:a do ColorRect, que controla a transparencia.
func _fade_in() -> void:
# comeca invisivel
fundo.modulate.a = 0.0
var tween := create_tween()
tween.set_pause_mode(Tween.TWEEN_PAUSE_PROCESS)
tween.tween_property(fundo, "modulate:a", 1.0, 0.4)
O set_pause_mode(Tween.TWEEN_PAUSE_PROCESS) e o que faz o Tween ignorar a pausa do jogo. Sem isso, como pausamos a arvore no _ready, o fade nunca aconteceria. Animar modulate:a em vez de trocar a cor inteira tem a vantagem de afetar tambem os filhos do ColorRect, entao o texto e os botoes surgem junto com o fundo, tudo no mesmo movimento.
Se voce quiser um efeito de transicao mais elaborado entre cenas, como um circulo que fecha ou um corte com shader, vale conferir o tutorial de transicao de cena com fade, que usa a mesma ideia de Tween aplicada a troca de cenas inteiras.
Os botoes: reiniciar e voltar ao menu
Agora as duas acoes principais. Reiniciar deve recomecar o nivel atual, e o botao de menu deve levar de volta a tela inicial. Em ambos os casos, ha um detalhe critico: precisamos despausar o jogo antes de trocar de cena, senao a cena nova carrega ja congelada.
func _on_reiniciar() -> void:
get_tree().paused = false
get_tree().reload_current_scene()
func _on_menu() -> void:
get_tree().paused = false
get_tree().change_scene_to_file("res://cenas/menu_principal.tscn")
O reload_current_scene() recarrega a cena do zero, o que e a forma mais simples de reiniciar: tudo volta ao estado inicial porque o Godot destroi e recria todos os nodes. O change_scene_to_file() faz a troca para o menu. Ajuste o caminho res:// para onde a sua cena de menu realmente esta.
Reset do estado do jogo
Aqui mora uma armadilha. Se o seu jogo guarda dados em um singleton (Autoload), como pontuacao, vidas ou o checkpoint atual, recarregar a cena nao limpa esses dados. O reload_current_scene() so recria os nodes da arvore, mas o Autoload sobrevive entre cenas, e e justamente por isso que usamos ele para dados persistentes.
Entao, ao reiniciar, voce precisa decidir o que zerar de proposito. Imagine um Autoload chamado Jogo:
extends Node
var pontuacao: int = 0
var checkpoint_atual: Vector2 = Vector2.ZERO
var tem_checkpoint: bool = false
func resetar() -> void:
pontuacao = 0
checkpoint_atual = Vector2.ZERO
tem_checkpoint = false
E no botao de reiniciar, decida o comportamento. Se o jogo deve voltar do comeco do nivel, chame Jogo.resetar() antes de recarregar. Se voce tem um sistema de checkpoint e respawn e quer que o jogador volte para o ultimo ponto salvo em vez do inicio, ai voce nao reseta a posicao, so a vida:
func _on_reiniciar() -> void:
get_tree().paused = false
# mantem o checkpoint, mas zera a pontuacao do "run" atual
Jogo.pontuacao = 0
get_tree().reload_current_scene()
A escolha depende do seu design. O ponto importante e entender que recarregar a cena nao e a mesma coisa que resetar o estado global. Sao duas coisas separadas, e misturar elas e a origem de muitos bugs do tipo "minha pontuacao nao zera quando eu morro".
Aplicando o respawn no checkpoint
Se voce optou por manter o checkpoint, o player precisa nascer na posicao salva quando a cena recarrega. Como o Autoload sobreviveu, ele ainda tem o checkpoint_atual. No _ready do player, basta ler esse valor:
func _ready() -> void:
vida = vida_maxima
if Jogo.tem_checkpoint:
global_position = Jogo.checkpoint_atual
Assim, ao clicar em reiniciar, a cena recarrega, o player roda seu _ready, ve que existe um checkpoint salvo e se posiciona la. Para o jogador, parece que ele "voltou" ao ultimo ponto seguro, mesmo que por baixo dos panos a cena inteira tenha sido recriada.
Testando e ajustando
Antes de considerar pronto, rode o jogo e verifique alguns pontos: a tela aparece com fade suave, o jogo congela de verdade (inimigos param), os dois botoes respondem ao clique mesmo com a arvore pausada e o jogo despausa corretamente ao reiniciar ou voltar ao menu. Um teste rapido e levar dano de varios inimigos ao mesmo tempo e confirmar que a tela so aparece uma vez, gracas ao set_physics_process(false) que colocamos no morrer().
Se a tela nao aparece, o suspeito numero um e o process_mode: confira se o node raiz da GameOver esta em PROCESS_MODE_ALWAYS. Se o fade nao roda, falta o set_pause_mode no Tween. Esses dois detalhes sao a causa de quase todos os problemas com UI durante pausa no Godot 4.
Fechando o ciclo da tela game over godot
Com essas pecas no lugar, voce tem uma tela game over godot reutilizavel e desacoplada: o player so anuncia a morte por um sinal, a cena principal instancia a interface, o CanvasLayer garante que ela fique por cima de tudo, o Tween cuida do fade e os botoes resolvem reiniciar e voltar ao menu, com um cuidado explicito sobre o que faz parte do estado global do jogo. A partir daqui, da para evoluir: adicionar uma tela de vitoria com a mesma base, mostrar a pontuacao final no Label, ou animar os botoes surgindo um de cada vez. A estrutura nao muda, so cresce.


