Você criou um personagem, coletou itens, passou de nível e derrotou o primeiro chefe. O jogador está investido na jornada. Mas se ele fechar o jogo, todo esse progresso se perde. A capacidade de salvar e carregar o estado do jogo é o que transforma uma sessão de jogo em uma experiência contínua e recompensadora.
Em um post anterior, discutimos as diferentes formas de armazenar dados. Hoje, vamos focar na abordagem mais comum, flexível e poderosa para jogos single-player: usar o formato JSON.
JSON (JavaScript Object Notation) é um formato de texto leve, legível para humanos e, ao mesmo tempo, fácil para as máquinas interpretarem, tornando-o perfeito para guardar os dados do nosso jogo. Neste guia, criaremos um sistema de Save e Load do zero.
1. A Estrutura dos Dados: O que Vamos Salvar?
Antes de salvar qualquer coisa, precisamos definir quais dados são importantes. Faremos isso criando uma classe C# simples que servirá como um “contêiner” para todas as informações do nosso save.
- Crie um novo script C# chamado
GameData
, mas não o anexe a nenhum objeto. Este script não será umMonoBehaviour
. - Abra o script e substitua seu conteúdo por este:
// Remova ": MonoBehaviour" se ele foi adicionado automaticamente.
// Adicione a tag [System.Serializable] para que a Unity possa converter esta classe.
[System.Serializable]
public class GameData {
// Variáveis que queremos salvar.
public int nivelDoJogador;
public float saudeDoJogador;
public Vector3 posicaoDoJogador;
// Valores iniciais para um novo jogo.
public GameData() {
this.nivelDoJogador = 1;
this.saudeDoJogador = 100f;
this.posicaoDoJogador = Vector3.zero; // Posição inicial padrão
}3
}
A “tag” [System.Serializable]
é essencial. Ela sinaliza para a Unity que esta classe e suas variáveis públicas podem ser serializadas, ou seja, convertidas para um formato como o JSON. O construtor public GameData()
define os valores padrão para quando um jogador começa um novo jogo.
2. O Gerenciador de Save/Load (SaveManager.cs
)
Agora, vamos criar o cérebro do nosso sistema. Este será um Singleton, garantindo que tenhamos um ponto de acesso central para salvar e carregar o jogo de qualquer lugar do nosso código.
- Crie um novo script chamado
SaveManager
e anexe-o a um objeto vazio na cena (ex:SaveManagerObject
). - Implemente o padrão Singleton e as funções de Salvar e Carregar:
using UnityEngine;
using System.IO; // Essencial para lidar com arquivos!
public class SaveManager : MonoBehaviour {
public static SaveManager instance;
private string saveFilePath;
void Awake() {
// Configuração do Singleton
if (instance == null) {
instance = this;
DontDestroyOnLoad(gameObject);
} else {
Destroy(gameObject);
}
// Define o caminho do arquivo de save
saveFilePath = Path.Combine(Application.persistentDataPath, "savegame.json");
}
public void SalvarJogo(GameData data) {
// Converte o objeto de dados para uma string JSON
string json = JsonUtility.ToJson(data, true); // O 'true' formata o JSON para ser legível
// Escreve a string JSON no arquivo
File.WriteAllText(saveFilePath, json);
Debug.Log("Jogo salvo com sucesso em: " + saveFilePath);
}
public GameData CarregarJogo() {
// Verifica se o arquivo de save existe
if (File.Exists(saveFilePath)) {
// Lê o conteúdo do arquivo
string json = File.ReadAllText(saveFilePath);
// Converte a string JSON de volta para o objeto de dados
GameData data = JsonUtility.FromJson<GameData>(json);
Debug.Log("Jogo carregado com sucesso!");
return data;
} else {
Debug.LogWarning("Nenhum arquivo de save encontrado! Criando um novo jogo.");
// Retorna um novo GameData com valores padrão se não houver save
return new GameData();
}
}
}
Ponto importante: Usamos Application.persistentDataPath
. Este é o local mais seguro para salvar arquivos em qualquer plataforma (PC, Mac, mobile), pois é uma pasta onde seu jogo sempre terá permissão para escrever.
3. Colocando em Prática
Agora, como usamos isso? Você precisará de um script para coletar os dados do jogo e passá-los para o SaveManager
. Por exemplo, em um Player.cs
:
// Exemplo dentro do seu script do Jogador
public void SalvarEstado() {
GameData data = new GameData();
data.nivelDoJogador = this.nivel; // 'this.nivel' seria a variável de nível do seu jogador
data.saudeDoJogador = this.saudeAtual; // 'this.saudeAtual' seria a vida atual
data.posicaoDoJogador = transform.position;
SaveManager.instance.SalvarJogo(data);
}
public void CarregarEstado() {
GameData data = SaveManager.instance.CarregarJogo();
this.nivel = data.nivelDoJogador;
this.saudeAtual = data.saudeDoJogador;
transform.position = data.posicaoDoJogador;
}
Você pode então chamar essas funções a partir de botões no seu menu: SalvarEstado()
em um menu de pausa, e CarregarEstado()
no botão “Continuar” do menu principal.
4. Segurança e Próximos Passos
É importante notar que, por padrão, o arquivo JSON é um texto simples e pode ser facilmente editado pelo jogador.
Para jogos onde a trapaça é uma preocupação, o próximo passo seria criptografar a string JSON antes de salvá-la e descriptografá-la depois de carregá-la. Embora isso adicione complexidade, é uma camada de segurança essencial para proteger os dados do seu save.
Outras ideias para evoluir o sistema incluem a criação de múltiplos “slots” de save (salvando arquivos com nomes diferentes, como savegame_1.json
, savegame_2.json
, etc.).
Conclusão
Com este sistema, você deu ao seu jogo uma memória. O progresso do jogador agora tem valor e persistência, transformando sua criação em uma experiência muito mais envolvente.
Saber como estruturar e salvar dados é uma habilidade que separa protótipos rápidos de projetos de longo prazo. Foi um dos passos que me ajudou a pensar em meus jogos não como “mais um projeto”, mas como uma experiência contínua e completa para o jogador.
Quer ver um sistema de save sendo implementado em um projeto real? Em nosso canal no YouTube, a PERAI DEV, construímos mecânicas como esta do zero. Inscreva-se para não perder nenhum passo!