数据持久化常用的方案
在游戏开发中,数据持久化是指将游戏的状态或其他信息保存下来,以便在下一次游戏运行时能够恢复这些信息。这对于保持游戏进度、玩家偏好设置以及其他重要数据至关重要。那么, 都有哪些常用的游戏数据持久化方案呢?
1. PlayerPrefs
PlayerPrefs 是 Unity 提供的一个简单的键值对存储系统,适用于存储轻量级且非敏感的数据,例如玩家的分数、音量设置等。PlayerPrefs 的数据会随着应用程序被卸载而清除。
示例代码 :
1 2 3 4 5 6 PlayerPrefs.SetInt("HighScore" , highScore); PlayerPrefs.Save();int highScore = PlayerPrefs.GetInt("HighScore" , 0 );
2. 文件系统 (File I/O)
使用文件系统可以将数据以文件的形式保存在磁盘上,适用于保存较大的数据结构或二进制数据。可以通过序列化机制将复杂的数据结构转换成文件。
示例代码 :
1 2 3 4 5 6 string path = Application.persistentDataPath + "/savegame.dat" ; System.IO.File.WriteAllBytes(path, data);byte [] data = System.IO.File.ReadAllBytes(path);
3. JSON 序列化
JSON 是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。Unity 支持将对象序列化为 JSON 字符串,然后可以将 JSON 字符串写入文件或通过网络传输。
示例代码 :(下文有详细演示跳转 )
1 2 3 4 5 6 7 8 string json = JsonUtility.ToJson(gameState);string path = Application.persistentDataPath + "/savegame.json" ; System.IO.File.WriteAllText(path, json);string json = System.IO.File.ReadAllText(path); GameSaveData gameState = JsonUtility.FromJson<GameSaveData>(json);
4. XML 序列化
XML 是另一种常用的数据交换格式,虽然比 JSON 复杂一些,但在某些情况下可能更适用。Unity 支持使用 System.Xml
命名空间来序列化和反序列化 XML 数据。
5. 二进制序列化
对于非常大的数据集或需要高性能的应用场景,可以考虑使用二进制序列化。Unity 支持 BinaryFormatter
类来序列化对象。
代码示例位于下文跳转
6. SQLite 数据库
SQLite 是一个轻量级的嵌入式数据库引擎,非常适合用于移动设备和桌面应用。它可以用来存储复杂的数据结构,并支持 SQL 查询语言。
示例代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 using (var conn = new SQLiteConnection("Data Source=database.db;Version=3;" )) { conn.Open(); conn.Execute(@"CREATE TABLE IF NOT EXISTS Players ( ID INTEGER PRIMARY KEY AUTOINCREMENT, Name TEXT NOT NULL, Score INTEGER NOT NULL)" ); conn.Execute("INSERT INTO Players (Name, Score) VALUES (@name, @score)" , new { name = "Player1" , score = 100 }); var players = conn.Query<Player>("SELECT * FROM Players WHERE Score > @score" , new { score = 50 }); }
7. 云存储
对于多人在线游戏,可以考虑使用云存储服务来保存数据,如 Firebase、AWS S3 或 Azure Blob 存储等。
对比Json方案和二进制方案
JSON (JavaScript Object Notation)
优点
可读性强 :JSON 是一种基于文本的格式,可以直接阅读和调试。
跨平台兼容性 :JSON 被广泛支持,几乎所有的现代编程语言都有相应的库来处理 JSON 数据。
灵活性 :JSON 支持动态添加新的字段,即使客户端和服务器端的 JSON 结构发生变化,只要保持兼容的核心字段不变,通常不需要对现有代码做大的改动。
易于解析 :大多数语言都有内置的支持来解析 JSON 数据,因此解析起来相对容易。
扩展性 :JSON 支持复杂的数据类型,如嵌套的对象和数组。
缺点
空间占用大 :由于 JSON 是基于文本的,所以相比于二进制格式,它通常会占用更多的存储空间和网络带宽。
序列化/反序列化速度较慢 :文本格式的解析通常比二进制格式慢。
安全性较低 :JSON 数据容易被篡改,因为它可以直接被编辑。
二进制序列化
优点
紧凑高效 :二进制格式通常比 JSON 更紧凑,占用较少的存储空间和网络带宽。
序列化/反序列化速度快 :二进制数据的读写通常比文本格式快。
安全性较高 :二进制数据难以直接阅读和编辑,这增加了数据的安全性。
缺点
可读性差 :二进制数据很难直接阅读,这对于调试和故障排查来说是个挑战。
平台相关性 :二进制数据的解释可能会依赖于特定的平台和字节序,因此在不同平台上可能需要额外的工作来确保一致性。
兼容性问题 :当数据结构发生变化时,可能需要更新所有相关的序列化和反序列化代码。
灵活性较低 :二进制格式通常不支持动态添加新的字段,如果数据结构发生变化,可能需要重新定义整个序列化格式。
代码演示两种方案
假设有一个 Person
类,包含姓名和年龄两个字段。
1 2 3 4 5 6 7 8 9 10 11 public class Person { public string Name; public int Age; public Person (string name, int age ) { Name = name; Age = age; } }
JSON 序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 using UnityEngine;using System.IO;public static class JsonSerialization { public static void SerializeToJson () { Person person = new Person("Alice" , 30 ); string jsonString = JsonUtility.ToJson(person); Debug.Log(jsonString); File.WriteAllText("person.json" , jsonString); } public static Person DeserializeFromJson () { string jsonString = File.ReadAllText("person.json" ); return JsonUtility.FromJson<Person>(jsonString); } }
二进制序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 using UnityEngine;using System.IO;using System.Runtime.Serialization.Formatters.Binary;public static class BinarySerialization { public static void SerializeToBinary () { Person person = new Person("Bob" , 25 ); using (FileStream stream = File.Create("person.bin" )) { BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(stream, person); } } public static Person DeserializeFromBinary () { using (FileStream stream = File.OpenRead("person.bin" )) { BinaryFormatter formatter = new BinaryFormatter(); return (Person)formatter.Deserialize(stream); } } }
总结
如果需要跨平台兼容性和易于调试,且对网络带宽和存储空间的要求不高,那么 JSON 是更好的选择。
如果需要最小的存储空间和最快的序列化/反序列化速度,且能够接受二进制格式带来的不便,那么二进制序列化可能是更合适的选择。
另外, 在实际应用中,还可以考虑使用像 MessagePack 这样的高效二进制序列化库,它结合了 JSON 的易用性和二进制格式的高效性。
MessagePack简单示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 using UnityEngine;using System.IO;using System.Linq;using MessagePack; [MessagePackObject ]public class Person { [Key(0) ] public string Name; [Key(1) ] public int Age; public Person () { } public Person (string name, int age ) { Name = name; Age = age; } }public class MessagePackSerializationExample : MonoBehaviour { private void Start () { SerializeAndDeserialize(); } private void SerializeAndDeserialize () { Person person = new Person("Alice" , 30 ); byte [] messagePackData = MessagePackSerializer.Serialize(person); Person deserializedPerson = MessagePackSerializer.Deserialize<Person>(messagePackData); Debug.Log($"原始的 Person: Name={person.Name} , Age={person.Age} " ); Debug.Log($"反序列化后的 Person: Name={deserializedPerson.Name} , Age={deserializedPerson.Age} " ); } }
使用Json序列化的一些要注意的点
// TODO: 未完待续