15.1 为什么需要数据持久化
游戏数据持久化是指将运行时数据保存到磁盘,在下次运行时恢复。常见场景:
- 存档/读档
- 玩家设置(音量、画质)
- 排行榜分数
- 解锁进度
15.2 PlayerPrefs
PlayerPrefs 是最简单的持久化方式,以键值对形式存储。
存储数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| PlayerPrefs.SetInt("HighScore", 1000); PlayerPrefs.SetFloat("MusicVolume", 0.8f); PlayerPrefs.SetString("PlayerName", "ByteFisher"); PlayerPrefs.Save();
int highScore = PlayerPrefs.GetInt("HighScore", 0); float musicVol = PlayerPrefs.GetFloat("MusicVolume", 1f); string playerName = PlayerPrefs.GetString("PlayerName", "Player");
if (PlayerPrefs.HasKey("HighScore")) { }
PlayerPrefs.DeleteKey("HighScore"); PlayerPrefs.DeleteAll();
|
PlayerPrefs 存储位置
| 平台 |
路径 |
| Windows |
HKCU\Software\公司名\产品名(注册表)或 %APPDATA%\Unity |
| macOS |
~/Library/Preferences/com.公司名.产品名.plist |
| Android |
/data/data/包名/shared_prefs/ |
| iOS |
NSUserDefaults |
局限:PlayerPrefs 只能存基本类型,不适合复杂数据结构。
15.3 JSON 序列化
JSON 是 Unity 中推荐的数据持久化格式,支持复杂对象。
使用 JsonUtility
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
| using UnityEngine;
[System.Serializable] public class PlayerData { public string playerName; public int level; public float health; public Vector3 position; public int[] inventoryIds; }
public class SaveManager : MonoBehaviour { private string SavePath => Application.persistentDataPath + "/save.json";
public void Save(PlayerData data) { string json = JsonUtility.ToJson(data, prettyPrint: true); File.WriteAllText(SavePath, json); Debug.Log($"存档已保存到:{SavePath}"); }
public PlayerData Load() { if (File.Exists(SavePath)) { string json = File.ReadAllText(SavePath); return JsonUtility.FromJson<PlayerData>(json); } return null; }
public void DeleteSave() { if (File.Exists(SavePath)) { File.Delete(SavePath); } } }
|
使用 Newtonsoft.Json(功能更强大)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| using Newtonsoft.Json;
public class SaveManagerJsonNet : MonoBehaviour { public void Save(PlayerData data) { string json = JsonConvert.SerializeObject(data, Formatting.Indented); File.WriteAllText(SavePath, json); }
public PlayerData Load() { if (File.Exists(SavePath)) { string json = File.ReadAllText(SavePath); return JsonConvert.DeserializeObject<PlayerData>(json); } return null; } }
|
15.4 ScriptableObject
ScriptableObject 是一种数据容器,用于在编辑器中创建和配置数据资产。
定义数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| [CreateAssetMenu(fileName = "NewItem", menuName = "Game/Item")] public class ItemData : ScriptableObject { public string itemName; public int itemID; [TextArea] public string description; public Sprite icon; public ItemType type; public int maxStack = 1; public GameObject pickupPrefab; }
public enum ItemType { Weapon, Armor, Consumable, Quest }
|
创建和使用
1 2 3 4 5 6 7 8 9 10 11 12 13
|
public class Inventory : MonoBehaviour { public List<ItemData> items = new List<ItemData>();
public void AddItem(ItemData item) { items.Add(item); Debug.Log($"获得物品:{item.itemName}"); } }
|
ScriptableObject 的优势
- 编辑器内配置,无需运行即可创建
- 数据与逻辑分离
- 多个对象可共享同一数据实例(节省内存)
- 支持继承和多态
15.5 存档系统设计
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
| [System.Serializable] public class GameSaveData { public string saveVersion; public string saveTime; public PlayerData player; public List<EnemyData> enemies; public List<string> completedQuests; public Dictionary<string, bool> unlockedAchievements; }
public class SaveSystem : MonoBehaviour { private const string SaveFileName = "save.sav"; private const int MaxSlots = 10;
public static string GetSlotPath(int slotIndex) { return $"{Application.persistentDataPath}/slot_{slotIndex}.sav"; }
public static void SaveToSlot(int slotIndex, GameSaveData data) { string json = JsonUtility.ToJson(data); string path = GetSlotPath(slotIndex); File.WriteAllText(path, json); }
public static GameSaveData LoadFromSlot(int slotIndex) { string path = GetSlotPath(slotIndex); if (File.Exists(path)) { string json = File.ReadAllText(path); return JsonUtility.FromJson<GameSaveData>(json); } return null; }
public static bool SlotExists(int slotIndex) { return File.Exists(GetSlotPath(slotIndex)); } }
|
15.6 本章小结
- PlayerPrefs 适合简单设置,复杂数据推荐 JSON
Application.persistentDataPath 是跨平台存储目录
JsonUtility.ToJson / FromJson 实现数据序列化
- ScriptableObject 是编辑器中配置数据的利器
- 设计多存档位系统需规划好数据结构
练习题
- 使用 PlayerPrefs 保存音乐音量和画质设置
- 实现角色位置和生命值的 JSON 存档/读档
- 创建一个 ScriptableObject 武器数据表
- 设计一个 3 个存档位的系统,支持保存时间显示