Voltar para o Blog
Quest Log

PlayerPrefs na Unity: Salvar Recorde e Opções

Placar de recorde de jogo arcade ao lado de controles deslizantes de volume e qualidade numa tela de opções

Unity PlayerPrefs na prática: salvar recorde e opções com SetInt, GetFloat e HasKey, onde os dados ficam em cada plataforma e quando trocar por save em JSON.

Ferramenta:UnityC#

PlayerPrefs na Unity: Salvar Recorde e Opções

PlayerPrefs na Unity é um armazenamento simples de chave e valor para guardar preferências entre sessões: o recorde, o volume da música, a qualidade gráfica escolhida. Com PlayerPrefs.SetInt("HighScore", 1200) você grava, com PlayerPrefs.GetInt("HighScore", 0) você lê na próxima vez que o jogo abrir. Este tutorial de Unity PlayerPrefs monta o recorde e a tela de opções, mostra onde os dados ficam em cada plataforma e traça a linha de quando parar de usar PlayerPrefs e partir para um save de verdade.

As informações de armazenamento seguem a referência oficial do Unity 6.3 LTS, conferida em outubro de 2026.

O que é PlayerPrefs na Unity

PlayerPrefs é uma classe estática que guarda três tipos de valor: int, float e string. Cada valor tem uma chave em texto. Os métodos são poucos:

  • SetInt, SetFloat, SetString: gravam um valor numa chave.
  • GetInt, GetFloat, GetString: leem, com um valor padrão opcional para quando a chave não existe.
  • HasKey: diz se a chave existe.
  • DeleteKey e DeleteAll: apagam uma chave ou todas.
  • Save: força a gravação no disco.

O nome entrega a intenção: player preferences. Foi pensado para configurações do jogador, não para o progresso do jogo. Essa distinção é o fio do post inteiro.

Não existe SetBool. Para booleanos, a convenção é gravar 1 ou 0 com SetInt.

Salvando e lendo recorde

O recorde é o caso de uso perfeito: um número, que muda pouco, e que precisa sobreviver ao jogo fechar. Arquivo HighScore.cs:

using UnityEngine;

public class HighScore : MonoBehaviour
{
    private const string HighScoreKey = "HighScore";

    [SerializeField] private int currentScore;

    public int Best { get; private set; }

    private void Awake()
    {
        Best = PlayerPrefs.GetInt(HighScoreKey, 0);
    }

    public void AddPoints(int amount)
    {
        currentScore += amount;
    }

    public bool SubmitFinalScore()
    {
        if (currentScore <= Best)
        {
            return false;
        }

        Best = currentScore;
        PlayerPrefs.SetInt(HighScoreKey, Best);
        PlayerPrefs.Save();
        return true;
    }
}

Decisões que importam:

  • A chave numa constante. Digitar "HighScore" em dois lugares e errar uma letra em um deles gera um bug silencioso: o jogo grava numa chave e lê de outra. Com const, o compilador cuida disso.
  • Valor padrão no GetInt. Na primeira vez que o jogo abre, a chave não existe e o GetInt devolve o padrão (0). Sem precisar de HasKey.
  • Save só no fim da partida. O manual avisa que gravar pode causar engasgos e recomenda não chamar Save durante o gameplay. A tela de fim de partida é o momento natural. A Unity também grava sozinha no OnApplicationQuit, mas um fechamento forçado (o celular matando o app, um travamento) pode pular essa etapa, por isso o Save explícito no momento certo.

Se você já fez isso no Godot, o raciocínio é o mesmo do post sobre pontuação e recorde no Godot, com a diferença de que lá o recorde costuma ir para um arquivo.

Salvando opções de volume e qualidade

Opções são o outro uso clássico. Arquivo GameSettings.cs:

using UnityEngine;

public class GameSettings : MonoBehaviour
{
    private const string MasterVolumeKey = "Settings.MasterVolume";
    private const string QualityKey = "Settings.Quality";
    private const string FullscreenKey = "Settings.Fullscreen";

    public float MasterVolume { get; private set; }
    public int QualityLevel { get; private set; }
    public bool Fullscreen { get; private set; }

    private void Awake()
    {
        MasterVolume = PlayerPrefs.GetFloat(MasterVolumeKey, 0.8f);
        QualityLevel = PlayerPrefs.GetInt(QualityKey, QualitySettings.GetQualityLevel());
        Fullscreen = PlayerPrefs.GetInt(FullscreenKey, 1) == 1;
        Apply();
    }

    public void SetMasterVolume(float value)
    {
        MasterVolume = Mathf.Clamp01(value);
        AudioListener.volume = MasterVolume;
        PlayerPrefs.SetFloat(MasterVolumeKey, MasterVolume);
    }

    public void SetQuality(int level)
    {
        int maxLevel = QualitySettings.names.Length - 1;
        QualityLevel = Mathf.Clamp(level, 0, maxLevel);
        QualitySettings.SetQualityLevel(QualityLevel, true);
        PlayerPrefs.SetInt(QualityKey, QualityLevel);
    }

    public void SetFullscreen(bool value)
    {
        Fullscreen = value;
        Screen.fullScreen = value;
        PlayerPrefs.SetInt(FullscreenKey, value ? 1 : 0);
    }

    public void ConfirmAndSave()
    {
        PlayerPrefs.Save();
    }

    private void Apply()
    {
        AudioListener.volume = MasterVolume;
        QualitySettings.SetQualityLevel(Mathf.Clamp(QualityLevel, 0, QualitySettings.names.Length - 1), true);
        Screen.fullScreen = Fullscreen;
    }
}

Ligue os métodos aos controles da UI: um Slider (de 0 a 1) chama SetMasterVolume no evento On Value Changed, um Dropdown chama SetQuality e um Toggle chama SetFullscreen. O botão "Voltar" do menu chama ConfirmAndSave.

Repare no prefixo Settings. nas chaves. Com dezenas de chaves, um prefixo por área evita colisão e facilita achar tudo no registro. E o Mathf.Clamp na qualidade protege contra um valor salvo numa versão anterior do jogo que tinha mais níveis de qualidade do que a atual.

Para volume por categoria (música, efeitos, voz), o caminho é um Audio Mixer com parâmetros expostos; o PlayerPrefs só guarda o número de cada um.

HasKey, DeleteKey e Save

HasKey serve quando o valor padrão não basta para decidir. O caso típico é "primeira vez que o jogador abre o jogo":

if (!PlayerPrefs.HasKey("Tutorial.Done"))
{
    // mostra o tutorial
    PlayerPrefs.SetInt("Tutorial.Done", 1);
    PlayerPrefs.Save();
}

DeleteKey apaga uma chave: útil para um botão "Zerar recorde". DeleteAll apaga tudo do jogo, o que você quer num botão "Restaurar padrões" ou durante o desenvolvimento.

No editor, há um atalho para limpar tudo: Edit > Clear All PlayerPrefs. Use antes de testar o fluxo de primeira execução. E lembre que o editor e a build guardam em lugares diferentes (próxima seção), então limpar no editor não limpa a build instalada.

Onde o PlayerPrefs fica no Windows, Mac, Android e WebGL

Segundo a referência do Unity 6.3:

PlataformaOnde fica
Windows (build)Registro: HKEY_CURRENT_USER\Software\NomeDaEmpresa\NomeDoProduto
Windows (editor)Registro: HKEY_CURRENT_USER\Software\Unity\UnityEditor\NomeDaEmpresa\NomeDoProduto
macOS (build)~/Library/Preferences/ num arquivo .plist com o identificador do app
Linux~/.config/unity3d/NomeDaEmpresa/NomeDoProduto
Android/data/data/nome-do-pacote/shared_prefs/nome-do-pacote.v2.playerprefs.xml (API SharedPreferences)
iOSAPI NSUserDefaults do sistema
WebIndexedDB do navegador, até 1 MB

NomeDaEmpresa e NomeDoProduto são os campos Company Name e Product Name em Edit > Project Settings > Player. Consequência prática: trocar o nome da empresa ou do produto depois de publicar faz o jogo procurar as preferências em outro lugar, e o jogador "perde" o recorde. Defina esses nomes no começo e não mexa mais.

Outro detalhe do Windows: no registro, a Unity acrescenta um hash ao nome da chave no registro (algo como HighScore_h1234567), para permitir chaves com diferença de maiúsculas e evitar conflito. Se for procurar no regedit, procure pelo começo do nome.

No Web, o limite de 1 MB e o fato de os dados ficarem no navegador significam que limpar os dados do site apaga tudo, e que abrir o jogo em outro navegador começa do zero.

Recorde e opções são a parte fácil do save. O resto (inventário, fases liberadas, posição, missões) é onde o projeto começa a exigir arquitetura de verdade, e é o ponto em que muita gente desiste do jogo por não saber estruturar dados que crescem junto com ele.

Curso de Unity
Quer fazer seu primeiro jogo na Unity?

No curso de Unity 6 da CursoGame.Dev você vai do zero ao primeiro jogo, com um desafio prático em cada level e suporte individual no WhatsApp quando travar.

Conhecer o curso de Unity
Suporte no WhatsAppAcesso vitalícioGarantia de 7 dias

Erros comuns com PlayerPrefs

Ler com o tipo errado. Gravou com SetFloat e leu com GetInt? A chave existe, mas com outro tipo, e o GetInt não devolve o número que você gravou (em geral volta o valor padrão). O recorde parece "zerado" sem nenhum erro no Console. Mantenha cada chave sempre com o mesmo tipo, e as constantes com o nome do tipo ajudam (VolumeFloat, por exemplo, se o projeto for grande).

Testar no editor e achar que a build vai lembrar. Editor e build gravam em chaves diferentes do registro. O recorde que você bateu testando no editor não aparece na build, e vice-versa. Não é bug, é o comportamento esperado.

Mudar Company Name ou Product Name depois de publicar. O jogo passa a ler outro caminho, e para o jogador parece que tudo foi apagado. Se for inevitável, leia as chaves antigas uma vez e copie para as novas numa atualização de transição.

Chamar Save a cada frame ou a cada moeda coletada. Funciona no seu PC rápido e engasga em celular mais simples. Grave nos momentos naturais: fim de fase, saída do menu de opções, pausa.

Esquecer o OnApplicationPause no mobile. No Android e no iOS o sistema pode matar o app em segundo plano sem passar pelo OnApplicationQuit. Um PlayerPrefs.Save() no OnApplicationPause(bool paused) quando paused for verdadeiro garante que as opções sobrevivam.

Chaves espalhadas pelo código. Com o projeto crescendo, juntar todas as chaves numa classe estática (PrefsKeys.HighScore, PrefsKeys.MasterVolume) evita duplicata e deixa fácil ver tudo que o jogo guarda.

Quando NÃO usar PlayerPrefs

PlayerPrefs é ótimo dentro do nome dele. Fora disso, atrapalha:

  • Progresso do jogo. Fases liberadas, inventário, moedas, posição. São dados estruturados (listas, objetos dentro de objetos), e PlayerPrefs só tem int, float e string. Você acaba inventando chaves como Item_0_Id, Item_0_Qtd, Item_1_Id... e qualquer mudança vira migração manual.
  • Dados que o jogador não deve editar. O manual diz que os dados ficam sem criptografia e pede para não guardar informação sensível ali. No Windows, qualquer um abre o regedit e muda o número de moedas.
  • Vários slots de save. Dá para simular com prefixos, mas um arquivo por slot é muito mais simples de listar, copiar e apagar.
  • Muito volume de dados. No Web, o teto é 1 MB. Em qualquer plataforma, guardar um JSON gigante numa string do registro é pedir problema.

A regra que uso: se o dado é uma escolha do jogador sobre como o jogo funciona (volume, idioma, controles, qualidade) ou um número solto (recorde), PlayerPrefs. Se é o estado do mundo do jogo, arquivo de save. No Godot a separação é a mesma, como mostra o post sobre sistema de save e load no Godot.

Próximo passo: save com JSON

Implemente o HighScore e o GameSettings deste post, feche e abra o jogo para confirmar que tudo volta como estava, e depois use Edit > Clear All PlayerPrefs para testar a primeira execução. Quando o seu jogo precisar guardar inventário, fases ou qualquer lista, siga para o sistema de save com JSON na Unity, que grava uma classe inteira num arquivo com versionamento.

Perguntas frequentes

Onde o PlayerPrefs fica salvo no Windows?

Numa build, no registro do Windows, em HKEY_CURRENT_USER\Software\NomeDaEmpresa\NomeDoProduto. Quando você testa no editor, a chave é outra: HKEY_CURRENT_USER\Software\Unity\UnityEditor\NomeDaEmpresa\NomeDoProduto. Os nomes vêm de Edit > Project Settings > Player.

PlayerPrefs é seguro para salvar moedas e progresso?

Não. O manual da Unity diz que o PlayerPrefs é guardado sem criptografia e recomenda não usá-lo para dados sensíveis. Qualquer jogador consegue editar o registro ou o arquivo de preferências. Para progresso, use um arquivo de save próprio.

Preciso chamar PlayerPrefs.Save?

A Unity grava automaticamente quando o jogo fecha normalmente (OnApplicationQuit). Chamar Save garante a gravação antes disso, o que protege contra fechamento forçado ou queda do aplicativo. O manual recomenda não chamar durante o gameplay, porque a gravação pode causar engasgos.

Qual a alternativa ao PlayerPrefs na Unity?

Para qualquer coisa além de preferências simples, a alternativa padrão é serializar uma classe de save para JSON com JsonUtility e gravar num arquivo em Application.persistentDataPath. Isso suporta listas, estruturas aninhadas e versionamento do save.