Explore o novo chatbot do Developer Center! O MongoDB AI chatbot pode ser acessado na parte superior da sua navegação para responder a todas as suas perguntas sobre o MongoDB .

Desenvolvedor do MongoDB
Central de desenvolvedor do MongoDBchevron-right
Idiomaschevron-right
C#chevron-right

Salvando dados no Unity3D usando o PlayPrefs

Dominic Frei11 min read • Published Feb 07, 2022 • Updated Sep 09, 2024
UnityRealmC#
Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Alguns recursos mencionados abaixo serão descontinuados em 30, 2025 de setembro. Saiba mais.
(Parte 1 da série de comparação de persistência)
A persistência de dados é uma parte importante da maioria dos jogos. A Unity oferece apenas um conjunto limitado de soluções, o que significa que também precisamos procurar outras opções.
Nesta série de tutoriais, exploraremos as opções oferecidas pela Unity e por bibliotecas de terceiros. Cada parte examinará mais profundamente uma delas, com a parte final sendo uma comparação:
  • Parte 1: PlayerPrefs (este tutorial)
  • Parte 2: Arquivos
  • Parte 3: BinaryReader e BinaryWriter (em breve)
  • Parte 4: SQL
  • Parte 5: Realm Unity SDK
  • Parte 6: Comparação de todas essas opções
Para facilitar o acompanhamento, preparamos um repositório de exemplo para você. Todos esses exemplos podem ser encontrados no mesmo projeto Unity, pois todos usam o mesmo jogo de exemplo, então você pode ver melhor as diferenças entre essas abordagens de persistência.
O repositório pode ser encontrado em https://github.com/realm/unity-examples, com este tutorial estando na ramificaçãopersistence-comparison ao lado de outros tutoriais que preparamos para você.

Exemplo de jogo

Observe que, se já tiver trabalhado em qualquer um dos outros tutoriais desta série, você pode pular esta seção, pois estamos usando o mesmo exemplo em todas as partes da série, para que seja mais fácil ver as diferenças entre as abordagens.
O objetivo desta série de tutoriais é mostrar a você uma maneira rápida e fácil de dar os primeiros passos nas várias formas de persistir os dados em seu jogo.
Portanto, o exemplo que usaremos será o mais simples possível no próprio editor para que possamos nos concentrar totalmente no código real que precisamos escrever.
Uma cápsula simples na cena será usada para que possamos interagir com um objeto de jogo. Em seguida, registramos os cliques na cápsula e mantemos a contagem de ocorrências.
Quando você abre um modelo de 3D limpo, basta escolher GameObject -> 3D Object -> Capsule.
Em seguida, você pode adicionar scripts à cápsula ativando-a na hierarquia e usando Add Component no inspetor.
Os scripts que adicionaremos a esta cápsula, apresentando os diferentes métodos, terão a mesma estrutura básica que pode ser encontrada em HitCountExample.cs.
1using UnityEngine;
2
3/// <summary>
4/// This script shows the basic structure of all other scripts.
5/// </summary>
6public class HitCountExample : MonoBehaviour
7{
8 // Keep count of the clicks.
9 [SerializeField] private int hitCount; // 1
10
11 private void Start() // 2
12 {
13 // Read the persisted data and set the initial hit count.
14 hitCount = 0; // 3
15 }
16
17 private void OnMouseDown() // 4
18 {
19 // Increment the hit count on each click and save the data.
20 hitCount++; // 5
21 }
22}
A primeira coisa que precisamos adicionar é um contador para os cliques na cápsula (1). Adicione um [SerializeField] aqui para que você possa observá-lo ao clicar na cápsula no editor Unity.
Sempre que o jogo for iniciado (2), queremos ler a contagem atual de acertos da persistência e inicializar hitCount de acordo (3). Isso é feito no método Start() que é chamado sempre que uma cena é carregada para cada objeto de jogo ao qual esse script está anexado.
A segunda parte disso é salvar as alterações, o que queremos fazer sempre que registrarmos um clique do mouse. A mensagem do Unity para isso é OnMouseDown() (4). Esse método é chamado toda vez que o GameObject ao qual esse script está anexado é clicado (com um clique do botão esquerdo do mouse). Nesse caso, incrementamos o hitCount (5) que, por fim, será salvo pelas várias opções mostradas nesta série de tutoriais.

PlayerPrefs

(Consulte PlayerPrefsExampleSimple.cs no repositório para ver a versão finalizada).
A maneira mais fácil e provavelmente a mais direta de salvar dados no Unity é usando o PlayerPrefsintegrado. A desvantagem, no entanto, é a usabilidade limitada, pois apenas três tipos de dados são suportados:
  • string
  • float
  • inteiro
Outro fato importante sobre eles é que eles salvam dados em texto sem formatação, o que significa que um jogador pode alterar facilmente seu conteúdo. Portanto, PlayerPrefs só deve ser usado para coisas como configurações gráficos, nomes de usuário e outros dados que podem ser alterados no jogo e, portanto, não precisam ser seguros.
Dependendo do sistema operacional em que o jogo está sendo executado, o PlayerPrefsserá salvo em locais diferentes. Todos eles estão listados na documentação. O Windows, por exemplo, usa o registro para salvar os dados em HKCU\Software\ExampleCompanyName\ExampleProductName.
O uso do PlayerPrefs é principalmente o mesmo que um dicionário. Eles são acessados como keyvalue pares / , em que key é do tipo string. Cada tipo de dados suportado tem sua própria função:
  • SetString(chave, valor)
  • GetString(key)
  • SetFloat(key, value)
  • GetFloat(key)
  • SetInt(chave, valor)
  • GetInt(key)
1using UnityEngine;
2
3public class PlayerPrefsExampleSimple : MonoBehaviour
4{
5 // Resources:
6 // https://docs.unity3d.com/ScriptReference/PlayerPrefs.html
7
8 [SerializeField] private int hitCount = 0;
9
10 private const string HitCountKey = "HitCountKey"; // 1
11
12 private void Start()
13 {
14 // Check if the key exists. If not, we never saved the hit count before.
15 if (PlayerPrefs.HasKey(HitCountKey)) // 2
16 {
17 // Read the hit count from the PlayerPrefs.
18 hitCount = PlayerPrefs.GetInt(HitCountKey); // 3
19 }
20 }
21
22 private void OnMouseDown()
23 {
24 hitCount++;
25
26 // Set and save the hit count before ending the game.
27 PlayerPrefs.SetInt(HitCountKey, hitCount); // 4
28 PlayerPrefs.Save(); // 5
29 }
30
31}
Para o exemploPlayerPrefs, criamos um script denominado PlayerPrefsExampleSimple com base no HitCountExample mostrado anteriormente.
Além da estrutura básica, também precisamos definir uma chave (1) que será usada para salvar o hitCount no PlayerPrefs. Vamos chamá-lo "HitCountKey".
Quando o jogo começa, primeiro queremos verificar se já havia uma contagem de acertos salva. OPlayerPrefs tem uma função embutida HasKey(hitCountKey) (2) que nos permite fazer exatamente isso. Se a chave existir, nós a lemos usando GetInt(hitCountKey) (3) e a salvamos no contador.
A segunda parte é salvar dados sempre que eles mudarem. Em cada clique depois de incrementarmos o hitCount, temos que chamar SetInt(key, value) em PlayerPrefs (4) para definir os novos dados. Observe que isso não salva os dados em disco. Isso só acontece durante OnApplicationQuit() implicitamente. Podemos escrever explicitamente os dados no disco a qualquer momento para evitar a perda de dados caso o jogo falhe e OnApplicationQuit() nunca seja chamado. Para escrever os dados no disco, chamamos Save() (5).

Exemplo estendido

(Consulte PlayerPrefsExampleExtended.cs no repositório para ver a versão finalizada).
Na segunda parte deste tutorial, estenderemos esta versão muito simples para ver maneiras de salvar dados mais complexos dentro PlayerPrefs.
Em vez de apenas detectar um clique do mouse, o script estendido detectará Shift+Click e Ctrl+Click também.
Novamente, para visualizar isso no editor, adicionaremos mais alguns [SerializeFields] (1). Substitua o atual (hitCount) pelo seguinte:
1// 1
2[SerializeField] private int hitCountUnmodified = 0;
3[SerializeField] private int hitCountShift = 0;
4[SerializeField] private int hitCountControl = 0;
Cada tipo de clique será mostrado em seu próprio elementoInspector.
O mesmo deve ser feito para as teclas PlayerPrefs . Remova oHitCountKey e adicione três novos elementos (2).
1// 2
2private const string HitCountKeyUnmodified = "HitCountKeyUnmodified";
3private const string HitCountKeyShift = "HitCountKeyShift";
4private const string HitCountKeyControl = "HitCountKeyControl";
Há muitas maneiras diferentes de salvar dados mais complexos. Aqui, usaremos três entradas diferentes em PlayerPrefs como primeira etapa. Mais tarde, veremos também como salvar dados estruturados que pertencem uns aos outros de uma maneira diferente.
Mais um campo que precisamos salvar é o KeyCode da tecla que foi pressionada:
1// 3
2private KeyCode modifier = default;
Ao iniciar a cena, o carregamento dos dados é semelhante ao exemplo anterior, apenas estendido por mais duas chamadas:
1private void Start()
2{
3 // Check if the key exists. If not, we never saved the hit count before.
4 if (PlayerPrefs.HasKey(HitCountKeyUnmodified)) // 4
5 {
6 // Read the hit count from the PlayerPrefs.
7 hitCountUnmodified = PlayerPrefs.GetInt(HitCountKeyUnmodified); // 5
8 }
9 if (PlayerPrefs.HasKey(HitCountKeyShift)) // 4
10 {
11 // Read the hit count from the PlayerPrefs.
12 hitCountShift = PlayerPrefs.GetInt(HitCountKeyShift); // 5
13 }
14 if (PlayerPrefs.HasKey(HitCountKeyControl)) // 4
15 {
16 // Read the hit count from the PlayerPrefs.
17 hitCountControl = PlayerPrefs.GetInt(HitCountKeyControl); // 5
18 }
19}
Como antes, primeiro verificamos se a chave existe no PlayerPrefs (4) e, em caso afirmativo, definimos o contador correspondente (5) para seu valor. Isso é bom para um exemplo simples, mas aqui você já pode ver que salvar dados mais complexos levará PlayerPrefs muito em breve aos seus limites se você não quiser escrever muitos códigos padrão.
O Unity oferece uma detecção para cliques do teclado e outras entradas, como um controlador ou o mouse, por meio de uma classe chamada Input. Usando GetKey, podemos verificar se uma tecla específica foi mantida pressionada no momento em que registramos um clique do mouse.
No entanto, a documentação nos informa sobre um fato importante:
Nota: os sinalizadores de entrada não são redefinidos até a atualização. Você deve fazer todas as chamadas de entrada no loop de atualização.
Portanto, também precisamos implementar a funçãoUpdate()(6) em que verificamos a chave e a salvamos no modifierdefinido anteriormente.
As chaves podem ser endereçadas por seu nome como string, mas a maneira segura de fazer isso é usar a classe KeyCode, que define todas as chaves necessárias. No nosso caso, seria KeyCode.LeftShift e KeyCode.LeftControl.
Essas verificações usam Input.GetKey() (7) e, se uma das duas for encontrada, ela será salva como modifier (8). Se nenhum deles tiver sido pressionado (9), acabamos de redefinir modifier para o default (10) que usaremos como marcador para um clique do mouse não modificado.
1private void Update() // 6
2{
3 // Check if a key was pressed.
4 if (Input.GetKey(KeyCode.LeftShift)) // 7
5 {
6 // Set the LeftShift key.
7 modifier = KeyCode.LeftShift; // 8
8 }
9 else if (Input.GetKey(KeyCode.LeftControl)) // 7
10 {
11 // Set the LeftControl key.
12 modifier = KeyCode.LeftControl; // 8
13 }
14 else // 9
15 {
16 // In any other case reset to default and consider it unmodified.
17 modifier = default; // 10
18 }
19}
O mesmo trio também pode ser encontrado na detecção de cliques:
1private void OnMouseDown()
2{
3 // Check if a key was pressed.
4 switch (modifier)
5 {
6 case KeyCode.LeftShift: // 11
7 // Increment the hit count and set it to PlayerPrefs.
8 hitCountShift++; // 12
9 PlayerPrefs.SetInt(HitCountKeyShift, hitCountShift); // 15
10 break;
11 case KeyCode.LeftControl: // 11
12 // Increment the hit count and set it to PlayerPrefs.
13 hitCountControl++; //
14 PlayerPrefs.SetInt(HitCountKeyControl, hitCountControl); // 15
15 break;
16 default: // 13
17 // Increment the hit count and set it to PlayerPrefs.
18 hitCountUnmodified++; // 14
19 PlayerPrefs.SetInt(HitCountKeyUnmodified, hitCountUnmodified); // 15
20 break;
21 }
22
23 // Persist the data to disk.
24 PlayerPrefs.Save(); // 16
25}
Primeiro, verificamos se um desses dois foi mantido pressionado enquanto o clique aconteceu (11) e, em caso afirmativo, incrementamos o contador de acertos correspondente (12). Caso contrário,13, o contadorunmodfied deverá ser incrementado (14).
Finalmente, precisamos definir cada um desses três contadores individualmente (15) via PlayerPrefs.SetInt() usando as três chaves que definimos anteriormente.
Como no exemplo anterior, também chamamos Save() (16) no final para garantir que os dados não sejam perdidos se o jogo não terminar normalmente.
Ao voltar para o editor do Unity, o script na cápsula agora deve ficar assim:

Dados mais complexos

(Consulte PlayerPrefsExampleJson.cs no repositório para ver a versão finalizada).
Nas duas seções anteriores, vimos como lidar com dois exemplos simples de persistência de dados em PlayerPrefs. E se eles se tornarem mais complexos do que isso? E se você quiser estruturar e agrupar dados?
Uma abordagem possível seria usar o fato de que PlayerPrefs pode conter um string e salvar um JSON lá.
Primeiro, precisamos descobrir como realmente transformar nossos dados em JSON. A estrutura .NET e a estrutura UnityEngine oferecem um serializador e um desserializador JSON para fazer esse trabalho para nós. Ambos se comportam de forma muito semelhante, mas usaremos o próprio JsonUtilitydo Unity, que temmelhor desempenho no Unity do que outras soluções JSON semelhantes.
Para transformar dados em JSON, primeiro precisamos criar um objeto container. Isso tem algumas restrições:
Internamente, esse método usa o serializador Unity. Portanto, o objeto que você passa deve ser suportado pelo serializador. Deve ser um MonoBehaviour, ScriptableObject ou classe/estrutura simples com o atributo Serializable aplicado. Os tipos de campos que você deseja incluir devem ser suportados pelo serializador; campos sem suporte serão ignorados, assim como campos privados, campos estáticos e campos com o atributo NonSerialized aplicado.
Em nosso caso, como estamos salvando apenas tipos de dados simples (int) por enquanto, tudo bem. Podemos definir uma nova classe (1) e chamá-la HitCount:
1// 1
2private class HitCount
3{
4 public int Unmodified;
5 public int Shift;
6 public int Control;
7}
Manteremos as lojas do editor Unity iguais (2):
1// 2
2[SerializeField] private int hitCountUnmodified = 0;
3[SerializeField] private int hitCountShift = 0;
4[SerializeField] private int hitCountControl = 0;
Todos esses serão eventualmente salvos no mesmo campoPlayerPrefs, o que significa que só precisamos de uma chave (3):
1// 3
2private const string HitCountKey = "HitCountKeyJson";
Como antes, o modifier indicará qual modificador foi usado:
1// 4
2private KeyCode modifier = default;
Em Start(), precisamos então ler o JSON. Como antes, verificamos se a chavePlayerPrefs existe (5) e depois lemos os dados, desta vez usando GetString() (em vez de GetInt() antes).
A transformação desse JSON no objeto real é então feita usando JsonUtility.FromJson() (6), que usa a string como um argumento. É uma função genérica e precisamos fornecer as informações sobre qual objeto esse JSON deve representar — neste caso, HitCount.
Se o JSON puder ser lido e transformado com sucesso, podemos definir os campos de contagem de acertos (7) para seus três valores.
1private void Start()
2{
3 // 5
4 // Check if the key exists. If not, we never saved to it.
5 if (PlayerPrefs.HasKey(HitCountKey))
6 {
7 // 6
8 var jsonString = PlayerPrefs.GetString(HitCountKey);
9 var hitCount = JsonUtility.FromJson<HitCount>(jsonString);
10
11 // 7
12 if (hitCount != null)
13 {
14 hitCountUnmodified = hitCount.Unmodified;
15 hitCountShift = hitCount.Shift;
16 hitCountControl = hitCount.Control;
17 }
18 }
19}
A detecção da tecla que foi pressionada é idêntica ao exemplo estendido, pois não envolve carregar ou salvar nenhum dado, mas é apenas uma verificação da tecla durante Update():
1private void Update() // 8
2{
3 // Check if a key was pressed.
4 if (Input.GetKey(KeyCode.LeftShift)) // 9
5 {
6 // Set the LeftShift key.
7 modifier = KeyCode.LeftShift; // 10
8 }
9 else if (Input.GetKey(KeyCode.LeftControl)) // 9
10 {
11 // Set the LeftControl key.
12 modifier = KeyCode.LeftControl; // 10
13 }
14 else // 11
15 {
16 // In any other case reset to default and consider it unmodified.
17 modifier = default; // 12
18 }
19}
De maneira muito semelhante, OnMouseDown() precisa salvar os dados sempre que forem alterados.
1private void OnMouseDown()
2{
3 // Check if a key was pressed.
4 switch (modifier)
5 {
6 case KeyCode.LeftShift: // 13
7 // Increment the hit count and set it to PlayerPrefs.
8 hitCountShift++; // 14
9 break;
10 case KeyCode.LeftControl: // 13
11 // Increment the hit count and set it to PlayerPrefs.
12 hitCountControl++; // 14
13 break;
14 default: // 15
15 // Increment the hit count and set it to PlayerPrefs.
16 hitCountUnmodified++; // 16
17 break;
18 }
19
20 // 17
21 var updatedCount = new HitCount
22 {
23 Unmodified = hitCountUnmodified,
24 Shift = hitCountShift,
25 Control = hitCountControl,
26 };
27
28 // 18
29 var jsonString = JsonUtility.ToJson(updatedCount);
30 PlayerPrefs.SetString(HitCountKey, jsonString);
31 PlayerPrefs.Save();
32}
Em comparação com antes, você vê que verificar a chave e aumentar o contador (13 - 16) permanece praticamente inalterado, exceto pela parte de salvar que agora está um pouco diferente.
Primeiro, precisamos criar um novo 17 HitCount ( ) e atribuir as três contagens. UsandoJsonUtility.ToJson(), podemos (18) criar uma string JSON a partir desse objeto e defini-la usando PlayerPrefs.
Lembre-se de também chamar Save() aqui para garantir que os dados não possam ser perdidos caso o jogo falhe sem que seja possível chamar OnApplicationQuit().
Execute o jogo e, depois de clicar na capsula algumas vezes com ou sem Shift e Controle, dê uma olhada no resultado. A captura de tela a seguir mostra o registro do Windows, que é onde os PlayerPrefs são salvos.
O local ao usar nosso projeto de exemplo é HKEY_CURRENT_USER\SOFTWARE\Unity\UnityEditor\MongoDB Inc.\UnityPersistenceExample e, como você pode ver, nosso JSON está bem aqui, salvo em texto sem formatação. Essa também é uma das grandes desvantagens a serem lembradas ao usar PlayerPrefs: os dados não estão seguros e podem ser editados facilmente quando salvos em texto sem formatação. Fique atento ao nosso futuro tutorial sobre criptografia, que é uma opção para melhorar a segurança dos seus dados.

Conclusão

Neste tutorial, vimos como salvar e carregar dados usando PlayerPrefs. Eles são muito simples e fáceis de usar e são uma ótima opção para alguns pontos de dados simples. Se ficar um pouco mais complexo, você poderá salvar dados usando vários campos ou agrupando-os em um objeto que pode ser serializado usando JSON.
O que acontece se você quiser persistir vários objetos da mesma classe? Ou várias turmas? Talvez com relacionamentos entre eles? E se a estrutura desses objetos mudar?
Como você pode ver, PlayerPrefs atinge seus limites muito rápido - por mais fáceis de usar e limitados que sejam.
Em tutoriais futuros, exploraremos outras opções para persistir dados no Unity e como elas podem resolver algumas ou todas as questões acima.
Forneça feedback e faça qualquer pergunta no fórum da comunidade do Realm.

Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Relacionado
Artigo

A versão 3.0 do driver C# está aqui! O que você precisa saber?


Nov 04, 2024 | 5 min read
Tutorial

Crie um website de notícias sobre criptomoedas em C# usando o Microsoft Azure App Service e o MongoDB Atlas


Jun 13, 2023 | 9 min read
Tutorial

Fundamentos do pipeline de agregação do MongoDB C#


Oct 11, 2024 | 5 min read
Tutorial

Integrando MongoDB com TensorFlow e C#


Sep 05, 2024 | 8 min read
Sumário
  • Exemplo de jogo