Voltar para o Blog
Quest Log

Como Criar um Menu de Pause no Godot 4: process_mode, CanvasLayer e Volume

Cena de jogo congelada ao fundo com um menu de pause em primeiro plano mostrando botões e um slider de volume

Aprenda a criar um menu de pause godot 4: pausar com get_tree().paused, process_mode certo, UI em CanvasLayer, ESC pra abrir e fechar e slider de volume.

Como Criar um Menu de Pause no Godot 4: process_mode, CanvasLayer e Volume

Menu de pause parece o tipo de coisa que se resolve em cinco minutos, até você tentar. Aí descobre que o jogo pausou mas o menu pausou junto, que o botão de "continuar" não responde a clique nenhum, ou que o ESC abre o menu mas nunca mais fecha. Todo mundo que faz um menu de pause godot pela primeira vez tropeça em pelo menos um desses três, e os três têm a mesma raiz: o sistema de pausa do Godot congela tudo por padrão, inclusive o código que deveria despausar.

A boa notícia é que o Godot 4 resolve isso com uma única propriedade, o process_mode, e entender ela é 80% do trabalho. Nesse tutorial eu monto um menu de pause completo: pausar a SceneTree com get_tree().paused, desenhar a UI por cima de tudo com CanvasLayer, abrir e fechar com ESC, e de bônus um slider de volume funcionando dentro do pause, porque é o ajuste que todo jogador espera encontrar ali. Todo código é GDScript do Godot 4.x.

Como a pausa funciona no Godot 4

Pausar o jogo no Godot é uma linha:

get_tree().paused = true

Quando paused vira true, a SceneTree para de chamar _process, _physics_process, _input e os demais callbacks de todos os nodes. Física congela, animações param, timers seguram a contagem. E é exatamente aí que mora o problema: o seu menu de pause também é um node da árvore, então ele congela junto. O botão de "continuar" não recebe clique, o ESC não é lido, e o jogo fica preso pausado pra sempre.

A saída é o process_mode, uma propriedade que todo node tem e que define como ele se comporta em relação à pausa. São cinco valores, mas na prática você usa três:

  • PROCESS_MODE_INHERIT: o padrão. O node copia o comportamento do pai. Como a raiz pausa, tudo pausa.
  • PROCESS_MODE_PAUSABLE: o node para quando a árvore pausa. É o comportamento que você quer pro mundo do jogo.
  • PROCESS_MODE_WHEN_PAUSED: o node só processa quando a árvore está pausada. Curioso, mas raro de usar.
  • PROCESS_MODE_ALWAYS: o node processa sempre, pausado ou não. É esse que o menu de pause precisa.
  • PROCESS_MODE_DISABLED: o node nunca processa.

A regra de ouro: o jogo fica em INHERIT (que herda pausável da raiz) e o menu de pause fica em ALWAYS. Assim, quando você pausa a árvore, o mundo congela e o menu continua vivo, recebendo input e cliques. Você configura isso no Inspector, na seção Node, Process, Mode, ou por código:

process_mode = Node.PROCESS_MODE_ALWAYS

Importante: process_mode se propaga pros filhos via INHERIT. Então basta marcar ALWAYS no node raiz do menu que os botões e sliders dentro dele funcionam também.

Estrutura da cena: CanvasLayer por cima de tudo

O menu de pause precisa aparecer na frente do jogo inteiro, sem depender de z-index nem de onde a câmera está. O node certo pra isso é o CanvasLayer: ele desenha numa camada própria, fixa na tela, acima do mundo 2D ou 3D. É o mesmo node que você usaria pra HUD, e se você já montou um menu principal no Godot, a lógica de Control nodes é idêntica, só muda o contexto.

A estrutura que eu uso:

PauseMenu (CanvasLayer)
└── Panel (Control, ancorado em tela cheia)
    └── VBoxContainer (centralizado)
        ├── TitleLabel (Label, "Pausado")
        ├── ResumeButton (Button, "Continuar")
        ├── VolumeLabel (Label, "Volume")
        ├── VolumeSlider (HSlider)
        └── QuitButton (Button, "Sair pro menu")

Dois ajustes no editor antes de qualquer script:

  1. No PauseMenu (o CanvasLayer), marque Process, Mode como Always no Inspector. Sem isso, nada do que vem a seguir funciona.
  2. No Panel, use o preset de âncora Full Rect pra cobrir a tela inteira. Um Panel escuro semitransparente por trás dos botões deixa claro que o jogo está pausado e ainda melhora a leitura do texto.

Salve essa cena como pause_menu.tscn e instancie ela na sua cena principal (ou num autoload de UI, se o projeto já tiver um). Por ser CanvasLayer, tanto faz onde ela entra na árvore: ela sempre desenha por cima.

Abrir e fechar com ESC

Primeiro, crie a ação no Input Map: Project Settings, Input Map, adicione uma ação chamada pause e vincule a tecla Escape. Se quiser suporte a controle, vincule também o botão Start. Eu já escrevi sobre como organizar o Input Map e os controles no Godot, e a regra de lá vale aqui: nunca cheque tecla direto no código, sempre passe por ação, porque remapear depois vira trivial.

O script do menu inteiro:

extends CanvasLayer

@onready var resume_button = $Panel/VBoxContainer/ResumeButton
@onready var quit_button = $Panel/VBoxContainer/QuitButton

func _ready():
    visible = false
    resume_button.pressed.connect(_on_resume_pressed)
    quit_button.pressed.connect(_on_quit_pressed)

func _unhandled_input(event):
    if event.is_action_pressed("pause"):
        toggle_pause()
        get_viewport().set_input_as_handled()

func toggle_pause():
    if get_tree().paused:
        despausar()
    else:
        pausar()

func pausar():
    get_tree().paused = true
    visible = true

func despausar():
    get_tree().paused = false
    visible = false

func _on_resume_pressed():
    despausar()

func _on_quit_pressed():
    get_tree().paused = false
    get_tree().change_scene_to_file("res://main_menu.tscn")

Alguns detalhes que valem explicação:

Por que _unhandled_input e não _input? Porque o _unhandled_input só recebe eventos que a UI não consumiu. Se um dia você tiver um campo de texto aberto, o ESC dele não vai vazar pro pause. E o set_input_as_handled() garante que mais ninguém reaja ao mesmo evento depois do menu.

Por que o ESC funciona com o jogo pausado? Porque o CanvasLayer está em PROCESS_MODE_ALWAYS, então o _unhandled_input dele continua sendo chamado mesmo com a árvore pausada. É o mesmo motivo pelo qual os botões respondem a clique. Se o seu menu pausa e não despausa, em 99% dos casos o process_mode ficou no padrão Inherit.

Por que despausar antes do change_scene_to_file? Porque a pausa é da SceneTree, não da cena. Trocar de cena com paused = true te leva pra um menu principal congelado, onde nada responde. Sempre zere a pausa antes de sair.

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

Slider de volume dentro do pause

Ajustar volume é o caso de uso número um de um menu de pause, e no Godot isso passa pelos audio buses. O bus Master existe em todo projeto por padrão, e mexer no volume dele afeta todo o áudio do jogo. O detalhe que derruba iniciante é a unidade: o mixer trabalha em decibéis, que são uma escala logarítmica, e o slider trabalha numa escala linear de 0 a 1. A conversão entre os dois é feita por linear_to_db e db_to_linear.

Configure o VolumeSlider no Inspector: Min Value em 0.0, Max Value em 1.0, Step em 0.01, Value em 1.0. Depois, adicione ao script do menu:

@onready var volume_slider = $Panel/VBoxContainer/VolumeSlider

var master_bus := AudioServer.get_bus_index("Master")

func _ready():
    visible = false
    resume_button.pressed.connect(_on_resume_pressed)
    quit_button.pressed.connect(_on_quit_pressed)
    volume_slider.value_changed.connect(_on_volume_changed)
    # Sincroniza o slider com o volume atual ao abrir o jogo.
    volume_slider.value = db_to_linear(AudioServer.get_bus_volume_db(master_bus))

func _on_volume_changed(value: float):
    AudioServer.set_bus_volume_db(master_bus, linear_to_db(value))

Com isso o slider controla o volume geral em tempo real, inclusive com o jogo pausado, porque os Control nodes do menu estão vivos via process_mode. Se o projeto tiver buses separados de música e efeitos, é só trocar o nome em get_bus_index e duplicar o slider. Eu detalho essa separação em buses e a mixagem completa no artigo sobre audio bus e mixagem no Godot.

Uma decisão de design que você precisa tomar: a música pausa junto com o jogo ou continua tocando? Por padrão, o AudioStreamPlayer pausa com a árvore. Se quiser que a música siga durante o pause (a maioria dos jogos faz isso, ou troca pra uma versão abafada), marque o node da música com PROCESS_MODE_ALWAYS também.

Armadilhas clássicas e como evitar

O timer que conta durante a pausa. O get_tree().create_timer(2.0) nasce com process_always = true, então ele ignora a pausa por padrão. Se um inimigo usa esse timer pra atacar, ele ataca com o jogo pausado. Passe false no segundo argumento (create_timer(2.0, false)) pra ele respeitar a pausa.

Input do player vazando. Se o player lê input em _unhandled_input e por algum motivo está em ALWAYS (acontece quando alguém copia configuração sem entender), ele se move com o jogo pausado. O mundo do jogo inteiro deve ficar em Inherit ou Pausable. O ALWAYS é exceção, não regra.

Pausar dentro de _physics_process de um node pausável. Se a lógica que chama toggle_pause() mora num node que congela com a pausa, ela nunca roda de novo pra despausar. Por isso o toggle vive no próprio menu, que está em ALWAYS.

Menu invisível mas bloqueando cliques. Se você esconde o menu mexendo só em modulate ou posição, os Controls continuam interceptando o mouse. Use visible = false, que desativa o desenho e o input de uma vez. Pra casos mais finos existe o mouse_filter, mas pro pause o visible resolve.

Tween que congela no meio. Tweens criados com create_tween() respeitam a pausa por padrão. Se uma animação do próprio menu de pause precisa rodar (um fade de entrada, por exemplo), configure tween.set_pause_mode(Tween.TWEEN_PAUSE_PROCESS) pra ela ignorar a pausa.

Fechando

Menu de pause no Godot 4 se resume a três peças: get_tree().paused congela o mundo, PROCESS_MODE_ALWAYS no CanvasLayer mantém o menu vivo, e _unhandled_input com uma ação do Input Map cuida do ESC. O slider de volume entra de graça em cima disso, porque o AudioServer não liga pra pausa.

Meu conselho pra fixar: monte o menu numa cena separada e teste os três caminhos, abrir com ESC, fechar com ESC e fechar pelo botão. Depois quebre de propósito, volte o process_mode pra Inherit e veja o menu travar. Entender por que ele trava vale mais do que copiar a configuração certa, porque o sistema de pausa aparece de novo em cutscene, em tela de inventário e em qualquer lugar onde o mundo precisa congelar e a UI não.