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:1gHighScore(number) valor padrão:0gVolMusica(number) valor padrão:0.8gVolSFX(number) valor padrão:0.8gSaveVersion(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.
| Dado | Chave | Tipo |
|---|---|---|
| Versão do save | meuJogo_saveVersion | number |
| Fase desbloqueada | meuJogo_faseDesbloqueada | number |
| High score | meuJogo_highScore | number |
| Volume música | meuJogo_volMusica | number |
| Volume SFX | meuJogo_volSFX | number |
Dica: se você tiver versões (demo, full) ou múltiplos perfis, inclua isso no prefixo: meuJogo_v1_ ou meuJogo_profile1_.
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
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= 3meuJogo_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 = 1gVolMusica = 0.8gVolSFX = 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 itempara 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 = 1gVolMusica = 0.8gVolSFX = 0.8gSaveVersion = 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çaSetItemcomgFaseDesbloqueada. - Se a chave for
meuJogo_volMusica, façaSetItemcomgVolMusica. - Se a chave for
meuJogo_volSFX, façaSetItemcomgVolSFX. - Se a chave for
meuJogo_saveVersion, façaSetItemcomgSaveVersion.
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
gVolMusicaegVolSFX
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.SetItemna 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, atualizegFaseDesbloqueada - 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
SetIteme noGetItemsã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 atualizemeuJogo_saveVersionpara a versão atual.
Assim, você consegue adicionar novas preferências no futuro sem quebrar jogadores antigos.