Voltar para o Blog
Quest Log

Menu de pausa no Godot 4: como criar do zero

Tela de um jogo 2D congelada ao fundo com um painel de menu semitransparente por cima contendo tres botoes empilhados na vertical

Aprenda a criar um menu de pausa no Godot 4 com get_tree().paused, process_mode e CanvasLayer. Tutorial passo a passo em GDScript tipado.

Criar um menu de pausa no Godot 4 parece trivial ate a hora que voce clica em Continuar e nada acontece: o botao esta la, o jogo esta congelado, mas o menu tambem congelou junto e ignora todo mundo. Esse e o erro classico de quem esta comecando, e a solucao passa por entender tres pecas que trabalham juntas: get_tree().paused, o process_mode de cada no e o CanvasLayer que segura a interface. Neste tutorial voce vai montar um menu de pausa funcional do zero, com botoes Continuar, Opcoes e Sair, tudo em GDScript tipado e usando a API atual do Godot 4.

A ideia central e simples. Voce congela o jogo inteiro com uma linha, mas diz para o Godot que o menu especifico e uma excecao: ele continua rodando enquanto o resto do mundo dorme. Vamos por partes.

Como o Godot 4 pausa o jogo

O Godot tem um sistema de pausa embutido na arvore de cena. Voce ativa assim:

get_tree().paused = true

Quando paused vira true, o Godot para de chamar _process(), _physics_process() e _input() na maioria dos nos. A fisica congela, as animacoes param, os inimigos ficam parados no ar. Para retomar, basta:

get_tree().paused = false

So que existe um detalhe crucial. Se TUDO congela, como o menu de pausa continua funcionando? Como ele recebe o clique no botao Continuar se get_tree().paused desligou o processamento? A resposta esta no process_mode.

process_mode: quem congela e quem continua

Todo no do Godot 4 tem uma propriedade chamada process_mode que decide como aquele no reage a pausa da arvore. Sao quatro valores relevantes (mais o padrao herdado):

  • Node.PROCESS_MODE_PAUSABLE: congela quando o jogo pausa. Esse e o comportamento padrao de fato para o gameplay. Seu player, seus inimigos e sua fisica devem ficar assim.
  • Node.PROCESS_MODE_ALWAYS: ignora a pausa e sempre processa. E exatamente o que o menu de pausa precisa, porque ele tem que responder a cliques enquanto o jogo esta congelado.
  • Node.PROCESS_MODE_WHEN_PAUSED: so processa quando o jogo esta pausado. Serve para efeitos que so existem durante a pausa, tipo um fundo escurecido animado.
  • Node.PROCESS_MODE_DISABLED: nunca processa, com pausa ou sem.

Existe ainda o Node.PROCESS_MODE_INHERIT, que e o valor inicial de um no novo: ele simplesmente herda o modo do no pai. Como a raiz da arvore comeca em modo pausavel, por herança quase tudo no seu jogo congela quando voce pausa. Isso e otimo, porque significa que voce so precisa marcar as excecoes.

E a regra de ouro deste tutorial: o menu de pausa fica num CanvasLayer com process_mode = Node.PROCESS_MODE_ALWAYS. Assim o jogo inteiro congela, mas o menu segue vivo, ouvindo teclado e mouse.

Por que usar um CanvasLayer

O CanvasLayer e um no que desenha seus filhos numa camada separada, por cima do mundo do jogo. Ele nao se move com a camera, nao sofre zoom e nao fica atras de nenhum sprite da cena. Isso resolve dois problemas de uma vez: a interface do menu aparece sempre na frente e sempre na mesma posicao da tela, independente de onde o player esteja no mapa.

A estrutura de nos que vamos montar fica assim:

MenuPausa (CanvasLayer)  -> process_mode = Always
└── Fundo (ColorRect)      cobre a tela e escurece o jogo
    └── Painel (VBoxContainer)
        ├── BotaoContinuar (Button)
        ├── BotaoOpcoes (Button)
        └── BotaoSair (Button)

O CanvasLayer de nome MenuPausa e quem recebe o process_mode como Always. Todos os filhos herdam esse comportamento automaticamente, entao os botoes tambem vao responder mesmo com o jogo pausado.

Montando a cena do menu

Crie uma cena nova com um CanvasLayer na raiz e renomeie para MenuPausa. No Inspector, procure a secao Process e mude o campo Mode de Inherit para Always. So esse clique ja garante que o menu inteiro escapa da pausa.

Dentro dele, adicione um ColorRect esticado para cobrir a tela toda (use o layout Full Rect no editor) e deixe uma cor preta com transparencia, por exemplo alpha 0.5, para dar aquele efeito de escurecer o jogo por tras. Depois adicione um VBoxContainer centralizado com tres nos Button: Continuar, Opcoes e Sair.

Por fim, deixe o CanvasLayer invisivel por padrao. A gente vai controlar a visibilidade por codigo. Uma forma limpa de esconder tudo e mexer na propriedade visible do proprio CanvasLayer.

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

O script do menu de pausa

Agora o codigo. Anexe este script ao no raiz MenuPausa. Ele controla a variavel de estado, alterna a pausa e liga os botoes. Tudo tipado, como manda o bom estilo no Godot 4:

extends CanvasLayer

var esta_pausado: bool = false

func _ready() -> void:
    # Garante que o menu comeca escondido e sempre processa.
    process_mode = Node.PROCESS_MODE_ALWAYS
    visible = false

func _process(_delta: float) -> void:
    # Captura o ESC (acao ui_cancel ja vem mapeada no Godot).
    if Input.is_action_just_pressed("ui_cancel"):
        alternar_pausa()

func alternar_pausa() -> void:
    esta_pausado = not esta_pausado
    get_tree().paused = esta_pausado
    visible = esta_pausado

Repare em tres coisas. Primeiro, no _ready() eu reforco o process_mode por codigo, mesmo tendo setado no editor, so para deixar a intencao explicita para quem ler o script depois. Segundo, o _process deste no funciona MESMO com o jogo pausado, justamente porque o modo dele e Always. E terceiro, a funcao alternar_pausa() inverte o estado, aplica em get_tree().paused e mostra ou esconde o CanvasLayer de uma vez so. Uma tecla para pausar e a mesma tecla para despausar.

O nome do parametro comeca com sublinhado (_delta) porque nao o usamos aqui, e essa e a convencao do Godot para evitar avisos de variavel nao utilizada.

Ligando os botoes Continuar, Opcoes e Sair

Falta conectar os tres botoes. Voce pode ligar os sinais pressed pelo editor ou pegar as referencias por codigo. Vamos pelo codigo, com @onready e tipagem em cada no:

@onready var botao_continuar: Button = $Fundo/Painel/BotaoContinuar
@onready var botao_opcoes: Button = $Fundo/Painel/BotaoOpcoes
@onready var botao_sair: Button = $Fundo/Painel/BotaoSair

func _ready() -> void:
    process_mode = Node.PROCESS_MODE_ALWAYS
    visible = false
    botao_continuar.pressed.connect(_ao_continuar)
    botao_opcoes.pressed.connect(_ao_abrir_opcoes)
    botao_sair.pressed.connect(_ao_sair)

func _ao_continuar() -> void:
    # Simplesmente retoma o jogo.
    esta_pausado = false
    get_tree().paused = false
    visible = false

func _ao_abrir_opcoes() -> void:
    # Aqui voce abriria seu painel de opcoes.
    print("Abrir menu de opcoes")

func _ao_sair() -> void:
    # Sempre despause antes de trocar de cena, senao a proxima nasce congelada.
    get_tree().paused = false
    get_tree().change_scene_to_file("res://menus/menu_principal.tscn")

O botao Continuar faz o mesmo que dar ESC de novo: despausa e esconde o menu. O botao Opcoes normalmente abre um segundo painel (som, resolucao, controles), que voce pode montar como outra cena filha. E o botao Sair leva de volta ao menu principal, mas repare no detalhe que derruba muita gente: sempre chame get_tree().paused = false antes de trocar de cena. Se voce sair com o jogo ainda pausado, a proxima cena carrega ja congelada e parece que o jogo travou.

Se voce ainda nao tem uma tela inicial para onde voltar, vale a pena montar uma junto com este menu. O passo a passo esta no nosso guia de como criar um menu principal no Godot, que usa a mesma logica de CanvasLayer e botoes que voce acabou de ver aqui.

Criando uma acao de pausa propria

Usar ui_cancel funciona porque essa acao ja vem no projeto mapeada para o ESC. Mas em muitos jogos voce quer que o botao Start do controle tambem pause, ou prefere uma tecla especifica. Nesse caso, crie sua propria acao no Project Settings, aba Input Map. De o nome de pausar, adicione a tecla ESC e, se quiser, o botao Start do gamepad.

Depois e so trocar a checagem no _process:

func _process(_delta: float) -> void:
    if Input.is_action_just_pressed("pausar"):
        alternar_pausa()

O Input.is_action_just_pressed() dispara uma unica vez no frame em que a tecla foi apertada, e nao continuamente enquanto ela fica pressionada. E exatamente o que voce quer para alternar pausa: um toque pausa, o proximo toque despausa. Se usasse is_action_pressed(), o jogo ficaria piscando entre pausado e ativo enquanto voce segura a tecla.

Cuidados que evitam dor de cabeca

Alguns pontos que valem ficar de olho depois que o basico funciona:

O primeiro e nao esquecer o process_mode do CanvasLayer. Se o menu aparece mas os botoes nao respondem, quase sempre e porque o modo ficou em Inherit e o menu congelou junto com o jogo. Confira no Inspector se esta em Always.

O segundo e o audio. Sons que estao tocando via AudioStreamPlayer tambem congelam se o no estiver em modo pausavel. Se voce quer que a musica de fundo continue durante a pausa, coloque aquele AudioStreamPlayer especifico em Always. E se quer um som de clique nos botoes do menu, o player desse som tambem precisa estar em Always, senao ele nao toca com o jogo parado.

O terceiro e a transicao de saida. Ao voltar para o menu principal, alem de despausar, e elegante fazer um efeito visual em vez de um corte seco. Da para combinar este menu com uma transicao de cena com fade no Godot para a saida ficar suave.

O quarto, para jogos com varias fases, e a persistencia. Do jeito que montamos, o menu de pausa vive dentro da cena atual e some quando voce troca de fase. Se quiser um menu unico e global, transforme o MenuPausa em um autoload (singleton), assim ele persiste entre as trocas de cena e voce acessa o menu de qualquer fase sem recria-lo toda vez.

Juntando tudo

O script final, enxuto e completo, fica assim:

extends CanvasLayer

var esta_pausado: bool = false

@onready var botao_continuar: Button = $Fundo/Painel/BotaoContinuar
@onready var botao_opcoes: Button = $Fundo/Painel/BotaoOpcoes
@onready var botao_sair: Button = $Fundo/Painel/BotaoSair

func _ready() -> void:
    process_mode = Node.PROCESS_MODE_ALWAYS
    visible = false
    botao_continuar.pressed.connect(_ao_continuar)
    botao_opcoes.pressed.connect(_ao_abrir_opcoes)
    botao_sair.pressed.connect(_ao_sair)

func _process(_delta: float) -> void:
    if Input.is_action_just_pressed("ui_cancel"):
        alternar_pausa()

func alternar_pausa() -> void:
    esta_pausado = not esta_pausado
    get_tree().paused = esta_pausado
    visible = esta_pausado

func _ao_continuar() -> void:
    esta_pausado = false
    get_tree().paused = false
    visible = false

func _ao_abrir_opcoes() -> void:
    print("Abrir menu de opcoes")

func _ao_sair() -> void:
    get_tree().paused = false
    get_tree().change_scene_to_file("res://menus/menu_principal.tscn")

Com esse arquivo anexado a um CanvasLayer em modo Always, mais o ColorRect e os tres botoes, voce tem um menu de pausa completo. Aperta ESC, o mundo congela, o menu aparece e responde. Aperta de novo ou clica em Continuar, e o jogo volta exatamente de onde parou.

O pulo do gato foi sempre o mesmo: get_tree().paused congela a arvore inteira, e o process_mode = Always do menu abre a excecao para que ele continue ouvindo voce. Entendida essa dupla, dava para fazer HUDs que reagem a pausa, telas de game over e ate um menu de opcoes que ajusta o som em tempo real. Se seu jogo salva progresso, o proximo passo natural e integrar o botao Sair com um sistema de save e load no Godot, para o jogador nunca perder o que conquistou ao pausar e fechar. Bons projetos, e continue construindo com a gente aqui no CursoGame.Dev.

Perguntas frequentes

Como pausar o jogo inteiro no Godot 4?

Use get_tree().paused = true para congelar a arvore de nos. Isso interrompe _process, _physics_process e a maioria dos sinais dos nos que estao no modo PAUSABLE. Para voltar, defina get_tree().paused = false.

Por que meu menu de pausa nao responde aos cliques quando o jogo esta pausado?

Porque o menu tambem congelou junto com o resto. Voce precisa definir o process_mode do no do menu como Node.PROCESS_MODE_ALWAYS, assim ele continua processando entrada e cliques mesmo com get_tree().paused ativo.

Qual a diferenca entre PAUSABLE, ALWAYS, WHEN_PAUSED e DISABLED?

PAUSABLE congela quando o jogo pausa (padrao para o gameplay). ALWAYS ignora a pausa e sempre processa (ideal para o menu). WHEN_PAUSED so processa quando o jogo esta pausado. DISABLED nunca processa.

Preciso de um CanvasLayer para o menu de pausa?

E altamente recomendado. O CanvasLayer desenha a interface por cima de tudo, ignora a camera do jogo e nao se move com o mundo. Coloque o menu de pausa dentro de um CanvasLayer com process_mode = Always.

Como capturar a tecla ESC para pausar no Godot 4?

Use Input.is_action_just_pressed("ui_cancel") dentro de _process ou _input. A acao ui_cancel ja vem mapeada para o ESC por padrao. Voce tambem pode criar uma acao propria chamada pausar no Input Map.

O menu de pausa some sozinho quando eu troco de cena?

Sim, se ele estiver dentro da cena atual. Se voce quer um menu de pausa unico e global para todas as fases, coloque-o em um autoload (singleton) para que ele persista entre as trocas de cena.