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.

Ferramenta:Godot

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). Se voce ainda nao tem essa parte pronta, vale montar antes um HUD de vida e municao no Godot, porque e ele que expoe o valor de vida que chega a zero e dispara o game over. 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.

Curso de Godot
Quer fazer seu primeiro jogo na Godot?

No curso de Godot 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 Godot
Suporte no WhatsAppAcesso vitalícioGarantia de 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 puxando de um sistema de score com recorde salvo, ou animar os botoes surgindo um de cada vez. A estrutura nao muda, so cresce. E como esta tela ja lida com pausa e process_mode, o proximo passo natural e montar o menu de pausa no Godot, que usa exatamente os mesmos conceitos pra congelar o jogo sem travar a interface.