🌟 方案核心思想
遵循以下設計原則:
- 數據安全第一:絕不使用明文存儲,采用AES加密算法保護數據。
- 性能優化:使用異步I/O操作,避免阻塞主線程導致游戲卡頓。
- 結構清晰:模塊化設計,職責分離,便于維護和擴展。
- 易于集成:提供單例入口,全局訪問方便。
🧱 模塊架構設計
整個數據持久化系統由四個核心模塊組成:
| 數據模型,定義可序列化的玩家數據結構 |
| 加密工具類,負責數據的加密與解密 |
| 異步文件操作工具,封裝讀寫邏輯 |
| 核心管理器,統一調度數據加載與保存 |
1?? 玩家數據模型:PlayerData.cs
using System;
using System.Collections.Generic;
using UnityEngine;[Serializable]
public class PlayerData
{public string playerName;public int level;public long gold;public List<string> inventoryItems; // 背包物品列表public Dictionary<string, int> equippedGear; // 裝備信息(部位 -> 物品ID)public int saveVersion; // 數據版本號,用于后續升級兼容// 構造函數,初始化默認值public PlayerData(string name, int lvl){playerName = name;level = lvl;gold = 0;inventoryItems = new List<string>();equippedGear = new Dictionary<string, int>();saveVersion = 1; // 初始版本}// 示例:添加物品public void AddItem(string item){inventoryItems.Add(item);}// 示例:升級public void LevelUp(){level++;Debug.Log($"玩家 {playerName} 升級到等級 {level}");}
}
2?? 安全加密:EncryptionUtility.cs
切記:永遠不要用明文存儲數據!
我們采用 AES(Advanced Encryption Standard) 對稱加密算法,結合CBC模式和PKCS7填充,確保數據安全。
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using UnityEngine;public static class EncryptionUtility
{// 🔐 演示用密鑰(256位 = 32字節)private static readonly byte[] Key = Encoding.UTF8.GetBytes("YourVeryStrongAndSecretKey123456");// 🔐 演示用IV(128位 = 16字節)private static readonly byte[] IV = Encoding.UTF8.GetBytes("AnotherSecretIV1");/// <summary>/// 加密字符串/// </summary>public static string Encrypt(string plainText){if (string.IsNullOrEmpty(plainText)) return string.Empty;using (Aes aesAlg = Aes.Create()){aesAlg.Key = Key;aesAlg.IV = IV;aesAlg.Mode = CipherMode.CBC;aesAlg.Padding = PaddingMode.PKCS7;ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);using (MemoryStream msEncrypt = new MemoryStream()){using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)){using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)){swEncrypt.Write(plainText);}}byte[] encryptedBytes = msEncrypt.ToArray();return Convert.ToBase64String(encryptedBytes); // 轉為Base64便于存儲}}}/// <summary>/// 解密字符串/// </summary>public static string Decrypt(string cipherText){if (string.IsNullOrEmpty(cipherText)) return string.Empty;byte[] cipherBytes;try{cipherBytes = Convert.FromBase64String(cipherText);}catch (FormatException){Debug.LogError("解密失敗:輸入字符串不是有效的Base64格式。");return string.Empty;}using (Aes aesAlg = Aes.Create()){aesAlg.Key = Key;aesAlg.IV = IV;aesAlg.Mode = CipherMode.CBC;aesAlg.Padding = PaddingMode.PKCS7;ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);using (MemoryStream msDecrypt = new MemoryStream(cipherBytes)){using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)){using (StreamReader srDecrypt = new StreamReader(csDecrypt)){return srDecrypt.ReadToEnd();}}}}}
}
3?? 異步文件操作:AsyncFileUtility.cs?
using UnityEngine;
using System.IO;
using System.Threading.Tasks;public static class AsyncFileUtility
{private static readonly string _saveDirectory = Application.persistentDataPath;/// <summary>/// 異步寫入加密數據/// </summary>public static async Task WriteAllTextAsync(string encryptedData, string fileName){string filePath = Path.Combine(_saveDirectory, fileName);try{await File.WriteAllTextAsync(filePath, encryptedData);Debug.Log($"數據已成功異步寫入到: {filePath}");}catch (System.Exception e){Debug.LogError($"異步寫入文件失敗({filePath}): {e.Message}");}}/// <summary>/// 異步讀取加密數據/// </summary>public static async Task<string> ReadAllTextAsync(string fileName){string filePath = Path.Combine(_saveDirectory, fileName);if (!File.Exists(filePath)){Debug.LogWarning($"文件不存在: {filePath}");return null;}try{string encryptedData = await File.ReadAllTextAsync(filePath);Debug.Log($"數據已成功異步從: {filePath} 讀取。");return encryptedData;}catch (System.Exception e){Debug.LogError($"異步讀取文件失敗({filePath}): {e.Message}");return null;}}
}
4?? 核心管理器:DataManager.cs?
?
using UnityEngine;
using System;
using System.Threading.Tasks;public class DataManager : MonoBehaviour
{public static DataManager Instance { get; private set; }private PlayerData _currentPlayerData;private const string PLAYER_SAVE_FILE = "playerSave.json";// 數據加載完成事件,用于通知其他模塊public static event Action<PlayerData> OnDataLoaded;void Awake(){if (Instance == null){Instance = this;DontDestroyOnLoad(gameObject); // 場景切換不銷毀}else{Destroy(gameObject);}}void Start(){LoadGameAsync(); // 啟動時自動加載}/// <summary>/// 異步加載游戲數據/// </summary>public async void LoadGameAsync(){Debug.Log("開始異步加載游戲數據...");string encryptedJson = await AsyncFileUtility.ReadAllTextAsync(PLAYER_SAVE_FILE);PlayerData loadedData = null;if (!string.IsNullOrEmpty(encryptedJson)){string jsonString = EncryptionUtility.Decrypt(encryptedJson);if (!string.IsNullOrEmpty(jsonString)){try{loadedData = JsonUtility.FromJson<PlayerData>(jsonString);// TODO: 可在此處添加版本兼容處理}catch (System.Exception e){Debug.LogError($"反序列化失敗: {e.Message}");}}}_currentPlayerData = loadedData ?? new PlayerData("新玩家", 1);Debug.Log(loadedData != null ? "加載存檔成功" : "創建新玩家");OnDataLoaded?.Invoke(_currentPlayerData);}/// <summary>/// 異步保存游戲數據/// </summary>public async void SaveGameAsync(){if (_currentPlayerData == null) return;Debug.Log("開始異步保存...");string jsonString = JsonUtility.ToJson(_currentPlayerData);string encryptedJson = EncryptionUtility.Encrypt(jsonString);await AsyncFileUtility.WriteAllTextAsync(encryptedJson, PLAYER_SAVE_FILE);Debug.Log("保存完成");}/// <summary>/// 獲取當前玩家數據/// </summary>public PlayerData GetCurrentPlayerData() => _currentPlayerData;// 建議在暫停或后臺時保存void OnApplicationPause(bool pauseStatus){if (pauseStatus) SaveGameAsync();}void OnApplicationQuit(){SaveGameAsync(); // 注意:異步可能無法保證完成}
}
在場景中創建一個空 GameObject(如命名為 DataManager
),并掛載 DataManager.cs
腳本。?
?