前言
如何在 Unity 中正确制作一个保存和加载系统,该系统使用JSON 文件来处理保存配置文件,可以保存和加载任何类型对象!标题为什么叫小型游戏存储功能呢?因为该存储功能可能只适合存储数据比较单一的情况,它非常的方便快捷,且易于使用和理解。但是如果你的游戏要存储的内容有很多,比如很多怪物的状态,很多宝箱的状态,很多物品的状态,那么它可能就不适用了。但是不用担心,后续我还会针对大型游戏,出更加复杂和全面的存储系统,解决存储数据比较多的情况。
存储
新增SaveProfile保存配置文件的泛型类和SaveProfileData抽象基类
[System.Serializable] // 保存配置文件的泛型类 public sealed class SaveProfile<T> where T : SaveProfileData { // 保存配置文件的名称 public string name; // 实际保存的数据 public T saveData; // 私有的默认构造函数,防止无参数实例化 private SaveProfile() { } // 公共构造函数,用于初始化名称和保存数据 public SaveProfile(string name, T saveData) { this.name = name; this.saveData = saveData; } } // 抽象基类,用于保存数据 public abstract record SaveProfileData {}
新增SaveManager,定义读取和存储 删除存档文件方法
using System; using System.IO; using UnityEngine; using Newtonsoft.Json; public static class SaveManager { // 文件保存的根目录路径 private static readonly string saveFolder = Application.persistentDataPath + "/GameData"; // 删除指定存档文件 public static void Delete(string profileName) { if (!File.Exists($"{saveFolder}/{profileName}")) throw new Exception($"保存配置文件 {profileName} 未找到!"); Debug.Log($"已成功删除 {saveFolder}/{profileName}"); File.Delete($"{saveFolder}/{profileName}"); } // 加载指定类型的存档文件 public static SaveProfile<T> Load<T>(string profileName) where T : SaveProfileData { if (!File.Exists($"{saveFolder}/{profileName}")) throw new Exception($"保存配置文件 {profileName} 未找到!"); // 读取文件内容为字符串 var fileContents = File.ReadAllText($"{saveFolder}/{profileName}"); // TODO:解密 Debug.Log($"已成功加载 {saveFolder}/{profileName}"); // 反序列化为指定类型的SaveProfile<T>对象并返回 return JsonConvert.DeserializeObject<SaveProfile<T>>(fileContents); } // 保存指定类型的存档数据 public static void Save<T>(SaveProfile<T> save) where T : SaveProfileData { if (File.Exists($"{saveFolder}/{save.name}")){ // throw new Exception($"保存配置文件 {save.name} 未找到!"); Delete(save.name); } // 将SaveProfile<T>对象序列化为JSON格式的字符串 var jsonString = JsonConvert.SerializeObject(save, Formatting.Indented, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }); // TODO:加密 if (!Directory.Exists(saveFolder)) Directory.CreateDirectory(saveFolder); // 将加密后的jsonString写入文件 File.WriteAllText($"{saveFolder}/{save.name}", jsonString); } }
使用
新增SaveData.cs,定义需要存储的数据,如果需要其他数据需要再添加
using UnityEngine; // 玩家保存数据 public record PlayerSaveData : SaveProfileData { // 玩家位置 public Vector2 position; // 成就数组 public int[] achievements; } // 世界保存数据 public record WorldSaveData : SaveProfileData { // 方块二维数组 public int[,] blocks; }
新增Player ,保存测试数据和读取
public class Player : MonoBehaviour { void Start() { Debug.Log(Application.persistentDataPath); // 保存玩家数据 var playerSave = new PlayerSaveData { position = new Vector2(1f, 1.5f), achievements = new[] { 1, 2, 3, 4, 5 } }; var saveProfile = new SaveProfile<PlayerSaveData>("playerSaveData", playerSave); SaveManager.Save(saveProfile); //保存世界数据 var worldSave = new WorldSaveData { blocks =new[,] { {1,1}, {1,2}, {1,3}} }; var saveProfile2 = new SaveProfile<WorldSaveData>("WorldSaveData", worldSave); SaveManager.Save(saveProfile2); } void Update() { //读取数据 if (Input.GetKeyDown(KeyCode.E)) { Vector2 position = SaveManager.Load<PlayerSaveData>("playerSaveData").saveData.position; Debug.Log(position); transform.position = position; } } }
运行之后,可以去查看保存的文件数据
运行按E成功加载数据
看到这里你应该就明白为什么叫小型游戏存储功能了吧!