Voltar para o Blog
Quest Log

Como Criar uma Tela de Game Over no Godot 4

Tela de game over de um jogo feito no Godot 4 com botoes de reiniciar e menu

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.

Próximo nível
Quer aprender isso na prática?

No CursoGame.Dev você sai dos tutoriais soltos e constrói jogos publicáveis, com trilha progressiva, quests práticas e feedback real.

Conhecer a plataforma
+500 alunos4.9/5Garantia 7 dias

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.