存档系统
1.JSON讲解
关于JSON的具体描述可以百度
2.JsonUtility
JsonUtility可以将可序列对象转化为Json格式的文件,那么,使用JsonUtility搭建存档系统的核心思路就是
第一步,将需要保存的数据转化位Json格式
第二步,将转换后Json文件写在某个路径下的一个文件夹中
当然读取的时候反过来就可以了
我们知道Json有一个优点,那就是直观易读,但是在存档系统中,Json的这个有点将成为致命的缺点,那就是其他人也是易读的,这样就会造成一种结果,那就是存档后,将存档文件修改,就可以做到开G
所以,为了避免这种情况,我们将使用AES进行加密,其他的加密方式也可以,具体看自己需求
下面开始存档系统的搭建
3.第一步存档
写一个函数,需要传递两个参数,一个是保存的存档文件的文件名,另一个是需要存档的数据文件,为object类型
第一步便是json转换
第二步是AES加密(该函数待会儿写)
第三步确定保存的路径
var path = Path.Combine(Application.persistentDataPath, saveFileName);
这行代码Path.Combine()用于将传入的两个参数结合
Application.persistentDataPath 是Unity提供的API,自动获取路径,详见Unity手册
saveFileName 是保存的文件名字
接下来便使用try catch语句来进行异常的捕获
public static void SaveByJson(string saveFileName, object data) { var json = JsonUtility.ToJson(data); var encryptedJson = EncryptString(key, json); // AES解密 var path = Path.Combine(Application.persistentDataPath, saveFileName); try { File.WriteAllText(path, encryptedJson); #if UNITY_EDITOR Debug.Log($"成功保存到 {path}."); #endif } catch(System.Exception exception) { #if UNITY_EDITOR Debug.Log($"文件保存到 {path}. \n{exception}"); #endif } }
复制
4.读取存档
具体不多讲,看代码
public static T LoadFromJson<T>(string saveFileName) { var path = Path.Combine(Application.persistentDataPath, saveFileName); try { //var json = File.ReadAllText(path); var encryptedJson = File.ReadAllText(path); var json = DecryptString(key, encryptedJson);// 使用解密函数解密数据 var data = JsonUtility.FromJson<T>(json); Debug.Log($"成功读取到 {path}."); return data; } catch(System.Exception exception) { #if UNITY_EDITOR Debug.Log($"文件从 {path} 加载. \n{exception}"); #endif return default; } }
复制
5.删除存档
public static void DeleteSaveFile(string saveFileName) { var path = Path.Combine(Application.persistentDataPath, saveFileName); try { File.Delete(path); Debug.Log($"成功删除到 {path}."); } catch(System.Exception exception) { #if UNITY_EDITOR Debug.Log($"文件从 {path} 删除. \n{exception}"); #endif } }
复制
6.AES加解密(采用16位key进行加解密)
/// <summary> /// AES加密函数 /// </summary> /// <param name="key"></param> /// <param name="plainText"></param> /// <returns></returns> public static string EncryptString(string key, string plainText) { byte[] iv = new byte[16]; byte[] array; using (Aes aes = Aes.Create()) { aes.Key = Encoding.UTF8.GetBytes(key); aes.IV = iv; ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV); using (MemoryStream memoryStream = new MemoryStream()) { using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write)) { using (StreamWriter streamWriter = new StreamWriter(cryptoStream)) { streamWriter.Write(plainText); } array = memoryStream.ToArray(); } } } return Convert.ToBase64String(array); } /// <summary> /// AES解密函数 /// </summary> /// <param name="key"></param> /// <param name="cipherText"></param> /// <returns></returns> public static string DecryptString(string key, string cipherText) { byte[] iv = new byte[16]; byte[] buffer = Convert.FromBase64String(cipherText); using (Aes aes = Aes.Create()) { aes.Key = Encoding.UTF8.GetBytes(key); aes.IV = iv; ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV); using (MemoryStream memoryStream = new MemoryStream(buffer)) { using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)) { using (StreamReader streamReader = new StreamReader(cryptoStream)) { return streamReader.ReadToEnd(); } } } } }
复制
7.Python随机生成AES的key
from Crypto.Random import get_random_bytes def generate_aes_key(key_length=16): """ 生成指定长度的 AES 密钥 参数: key_length: int,密钥长度,默认为 16 字节 返回值: bytes,生成的 AES 密钥 """ if key_length not in [16, 24, 32]: raise ValueError("AES密钥长度必须是16、24或32字节") return get_random_bytes(key_length) # 生成一个16字节(128位)的 AES 密钥 aes_key = generate_aes_key() print("生成的AES密钥:", aes_key.hex())
复制
8.整体代码
using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Security.Cryptography; using System.Text; using UnityEngine; namespace FilingSystem { /// <summary> /// 存档系统 /// </summary> public static class SaveSystem { private static string key = "b244a02444908deb248a0792ad540d25"; /// <summary> /// 保存数据 /// </summary> /// <param name="saveFileName">文件名,保存数据的文件名</param> /// <param name="data">需要保存的数据</param> public static void SaveByJson(string saveFileName, object data) { var json = JsonUtility.ToJson(data); var encryptedJson = EncryptString(key, json); // AES解密 var path = Path.Combine(Application.persistentDataPath, saveFileName); try { File.WriteAllText(path, encryptedJson); #if UNITY_EDITOR Debug.Log($"成功保存到 {path}."); #endif } catch(System.Exception exception) { #if UNITY_EDITOR Debug.Log($"文件保存到 {path}. \n{exception}"); #endif } } /// <summary> /// 读取数据 /// </summary> /// <param name="saveFileName">读取数据的文件名</param> /// <typeparam name="T">泛型</typeparam> /// <returns></returns> public static T LoadFromJson<T>(string saveFileName) { var path = Path.Combine(Application.persistentDataPath, saveFileName); try { //var json = File.ReadAllText(path); var encryptedJson = File.ReadAllText(path); var json = DecryptString(key, encryptedJson);// 使用解密函数解密数据 var data = JsonUtility.FromJson<T>(json); Debug.Log($"成功读取到 {path}."); return data; } catch(System.Exception exception) { #if UNITY_EDITOR Debug.Log($"文件从 {path} 加载. \n{exception}"); #endif return default; } } /// <summary> /// 删除存档 /// </summary> /// <param name="saveFileName"></param> public static void DeleteSaveFile(string saveFileName) { var path = Path.Combine(Application.persistentDataPath, saveFileName); try { File.Delete(path); Debug.Log($"成功删除到 {path}."); } catch(System.Exception exception) { #if UNITY_EDITOR Debug.Log($"文件从 {path} 删除. \n{exception}"); #endif } } /// <summary> /// AES加密函数 /// </summary> /// <param name="key"></param> /// <param name="plainText"></param> /// <returns></returns> public static string EncryptString(string key, string plainText) { byte[] iv = new byte[16]; byte[] array; using (Aes aes = Aes.Create()) { aes.Key = Encoding.UTF8.GetBytes(key); aes.IV = iv; ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV); using (MemoryStream memoryStream = new MemoryStream()) { using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write)) { using (StreamWriter streamWriter = new StreamWriter(cryptoStream)) { streamWriter.Write(plainText); } array = memoryStream.ToArray(); } } } return Convert.ToBase64String(array); } /// <summary> /// AES解密函数 /// </summary> /// <param name="key"></param> /// <param name="cipherText"></param> /// <returns></returns> public static string DecryptString(string key, string cipherText) { byte[] iv = new byte[16]; byte[] buffer = Convert.FromBase64String(cipherText); using (Aes aes = Aes.Create()) { aes.Key = Encoding.UTF8.GetBytes(key); aes.IV = iv; ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV); using (MemoryStream memoryStream = new MemoryStream(buffer)) { using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)) { using (StreamReader streamReader = new StreamReader(cryptoStream)) { return streamReader.ReadToEnd(); } } } } } } }
复制
9.测试代码
注意playerName的赋值,存档前和存档后playerName的值要一致
using System.Collections; using System.Collections.Generic; using FilingSystem; using TMPro; using UnityEngine; using UnityEngine.UI; public class SaveSystemTest : MonoBehaviour { [SerializeField] private string playerName; [SerializeField] private float score; [SerializeField] private Vector3 vector; [SerializeField] private Button saveButton; [SerializeField] private Button loadButton; [SerializeField] private Button deleteButton; [SerializeField] private TMP_Text scoreText; [SerializeField] private TMP_Text transformText; const string PLAYER_SAVE_FILENAME = "JackieText"; [System.Serializable]class PlayerData { public string playerName; //玩家名字 public float playerScore; //玩家得分 public Vector3 playerTransfrom; //玩家位置 } private void Start() { saveButton.onClick.AddListener(SaveButton); loadButton.onClick.AddListener(LoadButton); deleteButton.onClick.AddListener(DeleteButton); } // private void SaveButton() { PlayerData playerData = new PlayerData(); playerData.playerName = playerName; playerData.playerScore = score; playerData.playerTransfrom = vector; SaveSystem.SaveByJson(PLAYER_SAVE_FILENAME+playerName, playerData); } private void LoadButton() { var saveData = SaveSystem.LoadFromJson<PlayerData>(PLAYER_SAVE_FILENAME+playerName); scoreText.text = saveData.playerScore.ToString(); transformText.text = saveData.playerTransfrom.ToString(); } private void DeleteButton() { SaveSystem.DeleteSaveFile(PLAYER_SAVE_FILENAME); } }
复制