游戏中持久化数据一般可分为两种:
- 第一种是静态数据,例如Excel数据表中由策划人员编辑的数据,其特点是运行期间程序只需要读取,不需要修改;
- 第二种是存档数据,例如记录玩家在游戏过程中的进度,其特点是运行期间既需要读取,也需要修改,并且在版本升级的时候还需要考虑存档的删除或重置
Excel
策划人员通常都会在Excel中配置静态数据,例如道具表,它由主键、道具名称、描述、功能和参数等一系列数据组成。前后端使用道具主键来进行数据的通信,最终前端将主键所包含的整个数据信息展示在游戏中
EPPlus
EPPlus是一个可以有跨平台解析Excel能力第三方DLL库,目前已经升级到EPPlus5且收费
官网
Excel spreadsheet library for .NET Framework/Core - EPPlus Software
从网上找到比较古早的3.0.0.2,X86版本的DLL,直接放在了Assets——Plugins文件夹内
提供一个下载网址:epplus.dll File Download & Fix For All Windows OS (pconlife.com)
读取Excel
创建两个工作表,把工作簿命名为“test.xlsx”


根据Excel文件路径得到FileStream
,并且创建ExcelPackage
对象接着就可以用它对Excel进行读取了。
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
| using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using System.IO; using OfficeOpenXml;
public class ExcelLoader { [MenuItem("Excel/Load Excel")] static void LoadExcel() { string path = Application.dataPath + "/PersistentData/test.xlsx"; using(FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { using(ExcelPackage excel = new ExcelPackage(fs)) { ExcelWorksheets worksheets = excel.Workbook.Worksheets; for (int i = 1; i <= worksheets.Count; i++) { ExcelWorksheet worksheet = worksheets[i]; int colCount = worksheet.Dimension.End.Column; Debug.LogFormat("Sheet {0}", worksheet.Name); for (int row = 1,count = worksheet.Dimension.End.Row; row <= count; row++) { for(int col = 1;col <= colCount; col++) { var text = worksheet.Cells[row, col].Text ?? ""; Debug.LogFormat("下标:{0},{1} 内容:{2}", row, col, text); } } } } } } }
|
在导航栏中选择Excel→Load Excel命令,工作表1和工作表2相同的内容会合并。

写入Excel
首先,需要使用FileInfo来创建一个文件,接着使用ExcelPackage来向Excel文件中写入数据
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
| using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using System.IO; using OfficeOpenXml; using System;
public class ExcelCreator { [MenuItem("Excel/Write Excel")] static void CreateExcel() { string path = Application.dataPath + "/PersistentData/" + DateTime.Now.ToString("yyyy-MM-dd--hh-mm-ss") + ".xlsx"; var file = new FileInfo(path);
using(ExcelPackage excel = new ExcelPackage(file)) { ExcelWorksheet worksheet = excel.Workbook.Worksheets.Add("sheet1"); worksheet.Cells[1, 1].Value = "Company name1"; worksheet.Cells[1, 2].Value = "Address1";
worksheet = excel.Workbook.Worksheets.Add("sheet2"); worksheet.Cells[1, 1].Value = "Company name2"; worksheet.Cells[1, 2].Value = "Address2"; excel.Save(); } AssetDatabase.Refresh(); } }
|
创建完Excel为了在Unity中立刻看到效果,需要调用AssetDatabase.Refresh()
进行刷新

JSON
游戏运行时,我们是无法通过EPPlus读取Excel的,不过我们可以提前把Excel转化成可以运行时存取的格式,例如CSV、JSON和ScriptableObject等。
Unity支持JSON的序列化和反序列化。参与序列化的类必须在类上方声明[System.Serializable],并且支持类对象的相互嵌套(比如之前的PlayableAsset)。我们可以使用JsonUtility.ToJson()
以及JsonUtility.FromJson<T>()
来进行序列化以及反序列化。但是并不支持字典类型的序列化。
通过下面的脚本,将数据对象转成Json字符串,再从Json字符串还原数据对象
新建JsonDataProcessing
脚本
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 System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using System;
public class JsonDataProcessing { [MenuItem("Excel/Json")] static void LoadJson() { DataForJson dataForJson = new DataForJson(); dataForJson.name = "DataForJson"; dataForJson.subDatas.Add( new SubDataForJson { intValue = 1, boolValue = false, floatValue = 0.12f, stringValue = "abbb" }); dataForJson.subDatas.Add( new SubDataForJson { intValue = 2, boolValue = true, floatValue = 0.16f, stringValue = "abcd" });
string json = JsonUtility.ToJson(dataForJson); Debug.Log(json); dataForJson = JsonUtility.FromJson<DataForJson>(json); Debug.LogFormat("name = {0}", dataForJson.name); foreach (var item in dataForJson.subDatas) { Debug.LogFormat("intValue = {0},boolVaule = {1},floatValue = {2},stringValue = {3}", item.intValue, item.boolValue, item.floatValue, item.stringValue); } } } [Serializable] public class DataForJson { public string name; public List<SubDataForJson> subDatas = new List<SubDataForJson>(); }
[Serializable] public class SubDataForJson { public int intValue; public bool boolValue; public float floatValue; public string stringValue; }
|

JSON支持字典
Unity的JSON是不支持字典的,不过可以继承ISerializationCallBackReceiver
接口,间接实现字典序列化,此部分知识在之前的第四章(游戏脚本)脚本序列化讲到过
新建SerializableDictionaryTest
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor;
public class SerializableDictionaryTest { [MenuItem("Excel/Load Dictionary")] static void DictionaryTest() { SerializableDictionary<int, string> serializableDictionary = new SerializableDictionary<int, string>();
serializableDictionary[100] = "ATAO2017"; serializableDictionary[200] = "好好学习"; serializableDictionary[300] = "天天向上";
string json = JsonUtility.ToJson(serializableDictionary); Debug.Log(json);
serializableDictionary = JsonUtility.FromJson<SerializableDictionary<int, string>>(json); Debug.Log(serializableDictionary[100]); } }
|
SerializableDictionary
如下所示,序列化两个List元素来保存键和值,使用泛型Dictionary,这样键和值就更加灵活了,使用C#索引器来更方便地给字典赋值,然后在OnBeforeSerialize
和OnAfterDeserialize
进行序列化和反序列化赋值操作
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 45 46 47 48 49
| using System.Collections; using System.Collections.Generic; using UnityEngine;
public class SerializableDictionary<K,V> : ISerializationCallbackReceiver { [SerializeField] private List<K> m_Keys; [SerializeField] private List<V> m_Values;
private Dictionary<K, V> m_Dictionary = new Dictionary<K, V>();
public V this[K key] { get { if (!m_Dictionary.ContainsKey(key)) return default(V); return m_Dictionary[key]; } set { m_Dictionary[key] = value; } } public void OnAfterDeserialize() { int length = m_Keys.Count; m_Dictionary = new Dictionary<K, V>(); for (int i = 0; i < length; i++) { m_Dictionary[m_Keys[i]] = m_Values[i]; } m_Keys = null; m_Values = null; }
public void OnBeforeSerialize() { m_Keys = new List<K>(); m_Values = new List<V>();
foreach (var item in m_Dictionary) { m_Keys.Add(item.Key); m_Values.Add(item.Value); } } }
|