Construct do Zero: Menus e Fluxo de Jogo (Menu, Pause, Game Over e Vitória)

Capítulo 11

Tempo estimado de leitura: 9 minutos

+ Exercício

Objetivo do fluxo de jogo

Um jogo 2D bem organizado costuma ter um “ciclo” de telas: Menu → Fase → (Vitória ou Game Over) → Menu, com a possibilidade de Pausar durante a fase. Neste capítulo, você vai montar esse fluxo com transições consistentes (fade), navegação por teclado/mouse/touch e proteção contra cliques múltiplos (double click/tap), além de regras claras para reset de variáveis quando o jogador reinicia.

Layouts recomendados

  • Menu: botões “Jogar”, “Fases” (opcional) e “Sair” (opcional).
  • SelecaoFase: lista simples (Fase 1, Fase 2…) e botão “Voltar”.
  • Fase01 (e outras fases): gameplay.
  • OverlayPause: pode ser um layout separado (recomendado) ou uma camada/instância sobre a fase.
  • GameOver: “Tentar de novo” e “Menu”.
  • Vitoria: “Próxima fase” (se existir) e “Menu”.

Para manter consistência, use um padrão único de transição e um conjunto pequeno de variáveis globais para controlar navegação e bloqueios.

Variáveis globais para controle de fluxo

Crie variáveis globais (em Project) para padronizar o comportamento entre telas:

  • gNextLayout (string): nome do layout de destino (ex.: "Fase01").
  • gIsTransitioning (boolean ou number 0/1): bloqueia entradas durante transição.
  • gInputLockUntil (number): tempo (em segundos) até liberar clique/toque novamente.
  • gSelectedLevel (number): fase escolhida (ex.: 1, 2, 3).
  • gLastCheckpoint (string opcional): se você usar checkpoints.

Ideia central: toda troca de tela passa por uma função de transição que define gIsTransitioning e só troca de layout ao final do fade.

Camada/objeto de transição (Fade)

Opção A (simples e robusta): objeto “Fade” em todos os layouts

Em cada layout (Menu, SelecaoFase, Fase, GameOver, Vitoria), adicione um sprite chamado sprFade (um retângulo preto do tamanho da tela) numa camada no topo (ex.: Overlay). Configure:

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

Baixar o aplicativo

  • Cor: preto (ou outra).
  • Opacidade inicial: 100 no início do layout (para “fade in”).
  • Sem colisão, sem interação.

Adicione o behavior Fade ao sprFade (ou use Tween/Set opacity por eventos, se preferir). O objetivo é ter dois estados: fade-in (do preto para transparente) e fade-out (do transparente para preto).

Padrão de eventos para Fade-in ao iniciar qualquer layout

System: On start of layout  →  Set gIsTransitioning = 1  →  sprFade: Set opacity to 100  →  sprFade: Start fade to 0 (duration 0.35s)  →  Wait 0.35s  →  Set gIsTransitioning = 0

Isso garante que o jogador não clique em botões enquanto a tela ainda está aparecendo.

Padrão de eventos para Fade-out e troca de layout

Crie um grupo de eventos (ex.: Transitions) em uma Event Sheet compartilhada (ou copie para as telas). O fluxo:

Function Call: GoToLayout (param: layoutName)  →  If gIsTransitioning = 0  →  Set gIsTransitioning = 1  →  Set gNextLayout = layoutName  →  sprFade: Start fade to 100 (duration 0.35s)  →  Wait 0.35s  →  System: Go to layout gNextLayout

Se você não estiver usando Function, pode disparar por um evento comum: ao clicar em um botão, setar gNextLayout e iniciar o fade-out; ao terminar o fade, trocar de layout.

Prevenção de cliques múltiplos (mouse e touch)

Em menus, é comum o jogador clicar duas vezes e disparar duas transições. Use um bloqueio simples por tempo e por estado:

Bloqueio por estado (gIsTransitioning)

Em qualquer evento de clique/toque, adicione a condição gIsTransitioning = 0.

Bloqueio por tempo (gInputLockUntil)

Ao aceitar um clique, defina um pequeno cooldown:

On button clicked  →  If time >= gInputLockUntil  →  Set gInputLockUntil = time + 0.25

Combine com gIsTransitioning para ficar ainda mais seguro. Para touch, use o objeto Touch e eventos equivalentes (ex.: On tap gesture ou On touched object).

Menu principal: navegação por mouse, touch e teclado

Estrutura de botões

Use sprites para botões (ex.: btnPlay, btnLevels, btnQuit) e aplique feedback visual:

  • Ao passar o mouse: mudar animação/opacity/scale.
  • Ao pressionar: scale levemente menor.

Eventos de clique/toque

Mouse: On object clicked btnPlay  →  If gIsTransitioning = 0  →  Set gSelectedLevel = 1  →  Call GoToLayout("Fase01")
Mouse: On object clicked btnLevels  →  If gIsTransitioning = 0  →  Call GoToLayout("SelecaoFase")

Para touch:

Touch: On touched object btnPlay  →  If gIsTransitioning = 0  →  Set gSelectedLevel = 1  →  Call GoToLayout("Fase01")

Navegação por teclado (Enter/Esc e setas)

Uma forma simples é ter um índice de seleção (variável local do layout Menu: menuIndex) e destacar o botão selecionado.

On start of layout  →  Set menuIndex = 0
Keyboard: On Down pressed  →  If gIsTransitioning = 0  →  Set menuIndex = (menuIndex + 1) mod 2
Keyboard: On Up pressed  →  If gIsTransitioning = 0  →  Set menuIndex = (menuIndex - 1 + 2) mod 2
Every tick  →  btnPlay: Set highlighted if menuIndex=0; btnLevels: highlighted if menuIndex=1
Keyboard: On Enter pressed  →  If gIsTransitioning = 0  →  If menuIndex=0: Set gSelectedLevel=1; Call GoToLayout("Fase01")  →  If menuIndex=1: Call GoToLayout("SelecaoFase")

Se houver mais botões, ajuste o mod para a quantidade total.

Seleção simples de fase

Modelo de seleção minimalista

Crie botões btnLevel1, btnLevel2, btnBack. Ao clicar:

On clicked btnLevel1  →  If gIsTransitioning = 0  →  Set gSelectedLevel = 1  →  Call GoToLayout("Fase01")
On clicked btnLevel2  →  If gIsTransitioning = 0  →  Set gSelectedLevel = 2  →  Call GoToLayout("Fase02")
On clicked btnBack  →  If gIsTransitioning = 0  →  Call GoToLayout("Menu")

Carregar fase com base em gSelectedLevel (opcional)

Se preferir não duplicar eventos, você pode mapear a fase por nome:

On clicked btnLevelX  →  Set gSelectedLevel = X  →  Set gNextLayout = "Fase0" & str(X)  →  Call GoToLayout(gNextLayout)

Garanta que os nomes dos layouts sigam o padrão (Fase01, Fase02...).

Pause: congelar jogo e manter UI responsiva

Estratégia recomendada: camada “Pause” dentro da fase

Na fase, crie uma camada Pause acima de tudo, inicialmente invisível. Nela, coloque um painel escuro semitransparente e botões: btnResume, btnRestart, btnMenu.

Variável local de fase

Crie uma variável local no layout da fase: isPaused (0/1).

Eventos de pausar/despausar (teclado e botão)

Keyboard: On Esc pressed  →  If gIsTransitioning = 0  →  Toggle isPaused
System: isPaused = 1  →  Layer "Pause": Set visible  →  System: Set time scale to 0
System: isPaused = 0  →  Layer "Pause": Set invisible  →  System: Set time scale to 1

Observação importante: ao usar time scale = 0, alguns comportamentos/esperas podem parar. Por isso, evite usar Wait para lógica do pause. Botões de UI ainda podem funcionar, mas se notar travas, alternativa é pausar apenas grupos de eventos do gameplay (desabilitando o grupo “Gameplay”) e manter a UI ativa.

Botões do pause

On clicked btnResume  →  Set isPaused = 0
On clicked btnRestart  →  If gIsTransitioning = 0  →  Call ResetRunVariables  →  Call GoToLayout(layoutname)
On clicked btnMenu  →  If gIsTransitioning = 0  →  Call ResetRunVariables  →  Call GoToLayout("Menu")

layoutname é o nome do layout atual (reinicia a fase atual).

Regras claras de reset de variáveis

Para evitar bugs (vida não resetar, pontuação acumulando indevidamente, flags de vitória persistindo), defina uma regra: toda vez que iniciar uma fase “do zero”, chame um reset padrão.

O que resetar (exemplos comuns)

  • Vida/HP do jogador
  • Pontuação da fase (se não for acumulativa)
  • Moedas/coletáveis temporários
  • Flags: hasKey, bossDefeated, isInvincible
  • Tempo de fase (se existir)

Implementação com Function (recomendado)

Crie uma função ResetRunVariables em uma Event Sheet global (ou incluída nas telas que precisam). Exemplo de “esqueleto”:

Function: ResetRunVariables  →  Set PlayerHP = PlayerHPMax  →  Set Score = 0  →  Set Coins = 0  →  Set hasKey = 0  →  Set gLastCheckpoint = ""

Chame essa função ao:

  • Clicar em “Jogar” (se sempre começar do zero)
  • Clicar em “Tentar de novo”
  • Clicar em “Reiniciar” no pause
  • Voltar ao Menu após Game Over/Vitória (se você quiser limpar estado)

Game Over e Vitória: disparo e navegação

Disparando Game Over na fase

Quando a condição de derrota acontecer (ex.: vida chegou a 0), faça a transição:

System: PlayerHP <= 0  →  If gIsTransitioning = 0  →  Call GoToLayout("GameOver")

Se você usa animação de morte, pode esperar o fim da animação antes do fade-out (mas cuidado com múltiplos disparos: use gIsTransitioning como trava).

Disparando Vitória na fase

Quando o objetivo for cumprido (ex.: tocar no portal, coletar item final, derrotar boss):

Player overlaps objGoal  →  If gIsTransitioning = 0  →  Call GoToLayout("Vitoria")

Eventos no layout GameOver

  • Tentar de novo: reseta e volta para a fase selecionada.
  • Menu: reseta e volta ao menu.
On clicked btnRetry  →  If gIsTransitioning = 0  →  Call ResetRunVariables  →  Call GoToLayout("Fase0" & right("0" & str(gSelectedLevel), 2))
On clicked btnMenu  →  If gIsTransitioning = 0  →  Call ResetRunVariables  →  Call GoToLayout("Menu")

O trecho right("0" & str(gSelectedLevel), 2) ajuda a formar “01”, “02” etc. Ajuste ao seu padrão de nomes.

Eventos no layout Vitoria

Você pode oferecer “Próxima fase” se existir. Exemplo:

On clicked btnNext  →  If gIsTransitioning = 0  →  Add 1 to gSelectedLevel  →  Call ResetRunVariables  →  Call GoToLayout("Fase0" & right("0" & str(gSelectedLevel), 2))
On clicked btnMenu  →  If gIsTransitioning = 0  →  Call ResetRunVariables  →  Call GoToLayout("Menu")

Se não houver próxima fase, esconda o botão “Próxima” ou redirecione para o Menu.

Padronizando entradas: mouse, touch e teclado sem duplicar lógica

Uma forma prática de evitar repetir eventos é criar uma “ação de UI” por função, e chamar essa função a partir de diferentes entradas.

Exemplo: ação “Confirmar” no Menu

Function: MenuConfirm  →  If menuIndex=0: Set gSelectedLevel=1; Call ResetRunVariables; Call GoToLayout("Fase01")  →  If menuIndex=1: Call GoToLayout("SelecaoFase")

Chamadas:

Keyboard: On Enter pressed  →  Call MenuConfirm
Mouse: On clicked btnPlay  →  Set menuIndex=0  →  Call MenuConfirm
Touch: On touched btnPlay  →  Set menuIndex=0  →  Call MenuConfirm

Exercício guiado: ciclo completo com fade e reset

Meta do exercício

Implementar o ciclo: Menu → Fase01 → (Vitória ou GameOver) → Menu, com Pause dentro da fase, fade em todas as trocas e reset consistente.

Passo 1 — Preparar variáveis e função de reset

  • Crie gIsTransitioning, gNextLayout, gInputLockUntil, gSelectedLevel.
  • Crie a Function ResetRunVariables e resete tudo que representa “uma tentativa”.

Passo 2 — Adicionar o sprFade e eventos de fade-in

  • Em cada layout do ciclo (Menu, Fase01, GameOver, Vitoria), adicione sprFade na camada Overlay.
  • No On start of layout, execute o fade-in e libere gIsTransitioning ao final.

Passo 3 — Criar a ação padrão de troca de layout (fade-out)

  • Implemente GoToLayout(layoutName) com trava gIsTransitioning.
  • Garanta que nenhum botão chama Go to layout direto; sempre use a transição.

Passo 4 — Menu → Fase01

  • No botão “Jogar”: gSelectedLevel=1ResetRunVariablesGoToLayout("Fase01").
  • Adicione trava de clique: time >= gInputLockUntil e atualize gInputLockUntil.

Passo 5 — Pause dentro da fase

  • Crie camada Pause invisível.
  • Tecla Esc alterna isPaused.
  • Quando pausado: mostrar camada e pausar gameplay (time scale 0 ou desabilitar grupo Gameplay).
  • Botões: Retomar, Reiniciar (reset + recarregar fase), Menu (reset + Menu).

Passo 6 — Condições de vitória e derrota

  • Crie um gatilho simples de vitória (ex.: tocar em objGoal).
  • Crie um gatilho simples de derrota (ex.: variável PlayerHP chegar a 0, ou cair fora da tela).
  • Em ambos: checar gIsTransitioning=0 antes de chamar GoToLayout.

Passo 7 — GameOver/Vitoria → Menu

  • Em GameOver: botão Menu chama ResetRunVariables e GoToLayout("Menu").
  • Em Vitória: botão Menu faz o mesmo.

Checklist de validação (teste rápido)

TesteResultado esperado
Clicar rapidamente várias vezes em “Jogar”Apenas uma transição acontece
Apertar Enter durante o fade-in do MenuNenhuma ação até o fim do fade
Pausar e clicar em “Menu”Volta ao Menu com fade e sem estado “preso”
Perder (Game Over) e apertar “Tentar de novo”Fase reinicia com variáveis resetadas
Vencer e voltar ao MenuMenu abre limpo, sem travas de input

Agora responda o exercício sobre o conteúdo:

Ao implementar um fluxo com transições em fade, qual abordagem melhor evita que o jogador dispare múltiplas trocas de layout ao clicar/tocar rapidamente em botões do menu?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

A combinação de uma trava de estado (impede input durante o fade) com um cooldown por tempo (bloqueia double click/tap) reduz disparos duplicados e mantém as transições consistentes.

Próximo capitúlo

Construct do Zero: Efeitos Visuais (Partículas, Blend Modes e Animações de Feedback)

Arrow Right Icon
Capa do Ebook gratuito Construct do Zero: Desenvolvendo Jogos 2D Sem Programar (com Eventos)
61%

Construct do Zero: Desenvolvendo Jogos 2D Sem Programar (com Eventos)

Novo curso

18 páginas

Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.