Construct do Zero: Persistência de Dados (Save/Load) e Preferências

Capítulo 15

Tempo estimado de leitura: 9 minutos

+ Exercício

O que é persistência de dados (Save/Load) e por que usar

Persistência de dados é a capacidade do jogo de guardar informações entre sessões (fechar e abrir o jogo sem perder progresso). Em jogos 2D, isso normalmente inclui: progresso (fase desbloqueada), recordes (pontuação máxima) e preferências (volume de música e efeitos).

No Construct, o caminho mais comum é usar Local Storage, que grava dados no dispositivo/navegador do jogador. Você salva usando chaves (nomes únicos) e carrega essas chaves quando o jogo inicia.

O que você vai salvar (exemplos práticos)

  • faseDesbloqueada: número inteiro (ex.: 1, 2, 3...)
  • highScore: número inteiro
  • volMusica: número (0 a 1)
  • volSFX: número (0 a 1)
  • saveVersion: número inteiro para versionar o formato do save

Estrutura recomendada: variáveis globais e chaves de dados

Para manter o projeto organizado, use variáveis globais como “fonte da verdade” em tempo de execução e sincronize com o Local Storage quando necessário.

Variáveis globais sugeridas

  • gFaseDesbloqueada (number) valor padrão: 1
  • gHighScore (number) valor padrão: 0
  • gVolMusica (number) valor padrão: 0.8
  • gVolSFX (number) valor padrão: 0.8
  • gSaveVersion (number) valor padrão: 1

Chaves (keys) no Local Storage

Padronize nomes para evitar colisões e facilitar manutenção. Um padrão simples é prefixar com o nome do jogo.

DadoChaveTipo
Versão do savemeuJogo_saveVersionnumber
Fase desbloqueadameuJogo_faseDesbloqueadanumber
High scoremeuJogo_highScorenumber
Volume músicameuJogo_volMusicanumber
Volume SFXmeuJogo_volSFXnumber

Dica: se você tiver versões (demo, full) ou múltiplos perfis, inclua isso no prefixo: meuJogo_v1_ ou meuJogo_profile1_.

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

Serialização: salvar dados separados vs. salvar “pacote”

Opção A (mais simples): salvar cada variável em uma chave

Você grava cada valor em uma chave. É fácil de depurar e atualizar, mas gera mais operações de leitura/escrita.

  • meuJogo_faseDesbloqueada = 3
  • meuJogo_volMusica = 0.6

Opção B (mais robusta): salvar um objeto JSON em uma única chave

Você monta um “pacote” com tudo e salva como texto JSON. Isso facilita versionamento e migração, mas exige converter para texto e depois interpretar.

{ "version": 1, "fase": 3, "highScore": 12000, "volMusica": 0.6, "volSFX": 0.8 }

No Construct, você pode usar funções/expressões para montar strings e depois ler. Se preferir manter o capítulo prático e direto, a Opção A (chaves separadas) resolve a maioria dos casos e é a que vamos aplicar no exercício.

Passo a passo: salvando e carregando com Local Storage

1) Adicione o objeto Local Storage

No seu projeto, insira o objeto Local Storage (ele não precisa aparecer na tela). Ele fornece ações como Set item e Get item, além de condições de sucesso/erro.

2) Defina valores padrão (fallback) antes de carregar

Mesmo que você vá carregar do disco, comece com valores padrão. Assim, se não existir save (primeira execução) ou se houver erro, o jogo continua funcionando.

  • gFaseDesbloqueada = 1
  • gVolMusica = 0.8
  • gVolSFX = 0.8

3) Carregue no início do jogo e aplique imediatamente

O carregamento é assíncrono: você pede para ler e recebe o resultado em eventos de “On item get” (ou equivalente). Por isso, você deve: (1) pedir os itens no início, (2) quando cada item chegar, atualizar variáveis, (3) refletir na UI e no áudio.

Fluxo de eventos recomendado (visão geral)

  • No início do layout inicial: pedir Get item para cada chave.
  • Para cada retorno bem-sucedido: converter e atribuir à variável global.
  • Se a chave não existir/der erro: manter padrão e opcionalmente criar o item com padrão (auto-heal).
  • Após atualizar volumes: aplicar no sistema de áudio (música e SFX).
  • Após atualizar variáveis: atualizar textos/sliders da UI.

Versionamento de saves: evitando quebrar progresso em updates

Quando você atualiza o jogo, pode mudar o que é salvo (ex.: antes só havia volume, agora existe “sensibilidade”, “idioma”, etc.). Se você não versionar, um save antigo pode causar valores inválidos ou ausentes.

Estratégia simples de versionamento

  • Salve uma chave meuJogo_saveVersion.
  • Ao carregar, compare com gSaveVersion (a versão atual do jogo).
  • Se a versão for menor, aplique migrações (ex.: criar novas chaves com padrão).
  • Se for maior (raro), trate como incompatível e use padrão (ou ignore campos desconhecidos).

Exemplo de migração (conceito)

Suponha que na versão 1 você salvava apenas faseDesbloqueada e na versão 2 você adicionou volMusica e volSFX. Ao detectar saveVersion 1, você cria as novas chaves com valores padrão e atualiza saveVersion para 2.

Tratando ausência e corrupção de dados (valores inválidos)

Dois problemas comuns:

  • Ausência: a chave não existe (primeira vez jogando, cache limpo).
  • Corrupção/valor inválido: a chave existe, mas veio vazia, texto inesperado ou número fora do intervalo (ex.: volume 5).

Regras de validação (recomendadas)

  • Fase desbloqueada: mínimo 1; máximo = total de fases (se você tiver esse valor disponível)
  • Volumes: clamp entre 0 e 1
  • High score: mínimo 0

Mesmo sem “programar”, você consegue validar com comparações e usando expressões como min/max (ou eventos condicionais) para garantir limites.

Exercício guiado: salvar fase desbloqueada e volumes, carregar ao iniciar e refletir na UI

Objetivo: ao iniciar o jogo, carregar fase desbloqueada e volumes, atualizar a UI imediatamente e aplicar volumes no áudio. Ao mudar volumes ou desbloquear fase, salvar automaticamente.

Pré-requisitos do exercício

  • Um layout inicial (ex.: menu principal) onde o jogo começa.
  • Elementos de UI para mostrar/alterar volumes (ex.: textos, sliders ou botões +/−).
  • Um texto/indicador de fase desbloqueada (ex.: “Fase: X”).
  • Objeto Audio já configurado no projeto.
  • Objeto Local Storage adicionado.

1) Crie as variáveis globais e defina padrões

No painel de variáveis globais:

  • gFaseDesbloqueada = 1
  • gVolMusica = 0.8
  • gVolSFX = 0.8
  • gSaveVersion = 1

2) No início do layout inicial, peça os dados ao Local Storage

No Event Sheet do layout inicial (ou um sheet global), crie:

  • Evento: On start of layout
  • Ações: LocalStorage.GetItem("meuJogo_saveVersion"), GetItem("meuJogo_faseDesbloqueada"), GetItem("meuJogo_volMusica"), GetItem("meuJogo_volSFX")

Observação: você pode pedir tudo de uma vez; cada retorno virá em eventos separados.

3) Ao receber cada item, atribua e valide

Crie eventos para quando o Local Storage retornar valores. A ideia é: identificar qual chave voltou e aplicar.

Exemplo de lógica por chave (padrão de eventos)

  • Evento: Local Storage: On item get
  • Subcondição: LocalStorage.Key = "meuJogo_faseDesbloqueada"
  • Ações: converter para número e atribuir em gFaseDesbloqueada; validar mínimo 1.
  • Evento: Local Storage: On item get
  • Subcondição: LocalStorage.Key = "meuJogo_volMusica"
  • Ações: atribuir em gVolMusica; clamp 0..1.
  • Evento: Local Storage: On item get
  • Subcondição: LocalStorage.Key = "meuJogo_volSFX"
  • Ações: atribuir em gVolSFX; clamp 0..1.

Se o Construct retornar o valor como texto, garanta a conversão para número usando a expressão apropriada do engine (por exemplo, tratando como número ao atribuir). Se vier vazio, mantenha o padrão.

4) Se não existir item (primeira execução), grave os padrões (auto-criação)

Use o evento de erro/ausência do Local Storage (ex.: “On item missing”/“On item get failed”, dependendo da versão):

  • Se a chave for meuJogo_faseDesbloqueada, faça SetItem com gFaseDesbloqueada.
  • Se a chave for meuJogo_volMusica, faça SetItem com gVolMusica.
  • Se a chave for meuJogo_volSFX, faça SetItem com gVolSFX.
  • Se a chave for meuJogo_saveVersion, faça SetItem com gSaveVersion.

Isso garante que, após a primeira execução, os dados já existam para as próximas.

5) Aplique volumes no áudio assim que carregar (e também com padrão)

Crie um pequeno “aplicador” de configurações: sempre que gVolMusica ou gVolSFX mudar, aplique no Audio.

  • Ação típica: definir volume da tag/canal de música para gVolMusica.
  • Ação típica: definir volume da tag/canal de SFX para gVolSFX.

Se você usa tags (recomendado), mantenha consistência: por exemplo, todas as músicas com tag music e todos os efeitos com tag sfx.

6) Atualize a UI imediatamente após carregar

Assim que cada valor for carregado (ou mantido no padrão), atualize os elementos visuais:

  • Texto de fase: "Fase desbloqueada: " & gFaseDesbloqueada
  • Indicador de volume música: exibir round(gVolMusica*100) + “%”
  • Indicador de volume SFX: exibir round(gVolSFX*100) + “%”
  • Se usar slider/barra: definir posição/valor do slider para gVolMusica e gVolSFX

Importante: não espere o jogador abrir um menu para ver as configurações; carregue e reflita assim que o layout iniciar.

7) Salvar automaticamente quando o jogador mudar volumes

Quando o jogador clicar em botões de volume (ou arrastar slider):

  • Atualize gVolMusica / gVolSFX
  • Valide (clamp 0..1)
  • Aplique no áudio
  • Atualize UI
  • Faça LocalStorage.SetItem na chave correspondente

Isso evita depender de um botão “Salvar” e reduz risco de perda.

8) Salvar fase desbloqueada quando progredir

Quando o jogador desbloquear uma nova fase (por exemplo, ao vencer uma fase):

  • Se a fase atual + 1 for maior que gFaseDesbloqueada, atualize gFaseDesbloqueada
  • Salve: LocalStorage.SetItem("meuJogo_faseDesbloqueada", gFaseDesbloqueada)
  • Atualize UI (se estiver visível no momento)

Checklist de depuração rápida (quando “não carrega”)

  • As chaves usadas no SetItem e no GetItem são idênticas (mesma grafia)?
  • Você está carregando no layout correto (o que realmente inicia primeiro)?
  • Você tratou o caso de item ausente (primeira execução)?
  • O valor retornado está sendo convertido para número antes de validar/clamp?
  • Você atualiza UI e áudio no evento de retorno (e não apenas no start do layout)?

Extensão opcional: migrar saves por versão (modelo prático)

Se quiser aplicar versionamento já no exercício, use esta lógica:

  • Carregue meuJogo_saveVersion.
  • Se não existir, crie com gSaveVersion.
  • Se existir e for menor que gSaveVersion, crie chaves novas que faltam com padrão e então atualize meuJogo_saveVersion para a versão atual.

Assim, você consegue adicionar novas preferências no futuro sem quebrar jogadores antigos.

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

Ao carregar preferências e progresso com Local Storage no Construct, qual fluxo de eventos é o mais adequado para garantir que o jogo funcione na primeira execução e que UI/áudio reflitam os valores corretamente?

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

Você errou! Tente novamente.

O carregamento é assíncrono, então é recomendado iniciar com valores padrão e pedir os itens no começo. Quando cada item retornar, atualize as variáveis globais, valide (ex.: clamp 0..1) e aplique imediatamente na UI e no áudio; se faltar, mantenha o padrão e pode criar o item.

Próximo capitúlo

Construct do Zero: Organização Profissional de Eventos (Legibilidade e Escalabilidade)

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

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.