思想
在游戏过程中,玩家的背包、登录、人物系统都与数据息息相关,无论是一开始就设定好的默认数据,还是可以动态存取的数据,都需要开发人员去管理。
游戏开发过程中,策划一般通过Excel表格配置一些内容来对游戏的一些行为经行数据的设定。表格有config默认数据,程序只需要读取即可;还可能建立model类数据需要在游戏中实例化对象来进行数据的增删改查.
MVC架构中Model的CRUD操作也包含在存档类中(本地存档):
方法
excel转换成config默认数据(json文件)并通过对应的类读取数据可以参考我之前发的文章
https://www.cnblogs.com/ameC1earF/p/17270090.html
以下我对它进行了改良,涵盖了config默认数据以及类的转换以及model动态数据类文件的生成以及数据的存取。
使用1.写俩个Excel测试(这里同一个Excel分成俩份,一个表示默认配置数据,一个表示model的结构不带数据也可以的):
需要注意:
Excel存放路径:
Config导出路径(Resources.Json)以及存档存储路径(编辑模式下在Assets/Records下,运行模式下在Application.persistentDataPath中)
2.通过编辑器导出对应的类型:
导出的文件:
导出类路径以及导出类:
测试
本地存档也修改了:
存档的优化:
在实际开发中,游戏存档一般不会在每一次数据修改就会改变,而是选择在一个特殊阶段(比如玩家退出游戏),或者是间隔时间存储,所以我们
一般使用一个字典先记录模型和对应的数据,通过一个公共方法控制文件的存储。
完整代码
Json格式的数据类:
DataList
using System.Collections.Generic;using System;[Serializable]public class DataList{ public List datas = new List();}
导出类代码:
导出工具类
using UnityEngine;using UnityEditor;using System.IO;using OfficeOpenXml;using System.Collections.Generic;using System;using System.Text;/// /// 导出模式///
public enum ExporterMode{ /// /// 表格数据,策划配置的默认数据 ///
Config, /// /// 模型数据,服务器或者本地可以修改的数据 ///
Model,}/// /// 使用EPPlus获取表格数据,同时导出对应的Json以及Class.///
public class ExcelExporter{ /// /// ExcelConfig路径 ///
private const string excelConfigPath = "../Assets/Excels/Configs"; /// /// ExcelModel路径 ///
private const string excelModelPath = "../Assets/Excels/Models"; private const string configPath = "../Assets/Resources/Json"; private const string configClassPath = "../Assets/Scripts/Configs"; private const string modelPath = "../Assets/Records"; private const string modelClassPath = "../Assets/Scripts/Models"; /// /// 属性行 ///
private const int propertyIndex = 2; /// /// 类型行 ///
private const int typeIndex = 3; /// /// 值行 ///
private const int valueIndex = 4; [MenuItem("Tools/ExportExcelConfigs")] private static void ExportConfigs() { try { string path = string.Format("{0}/{1}", Application.dataPath, excelConfigPath); FileInfo[] files = FilesUtil.LoadFiles(path); foreach (var file in files) { //过滤文件 if (file.Extension != ".xlsx") continue; ExcelPackage excelPackage = new ExcelPackage(file); ExcelWorksheets worksheets = excelPackage.Workbook.Worksheets; //只导表1 ExcelWorksheet worksheet = worksheets[1]; ExportJson(worksheet, Path.GetFileNameWithoutExtension(file.FullName), ExporterMode.Config); ExportClass(worksheet, Path.GetFileNameWithoutExtension(file.FullName), ExporterMode.Config); } AssetDatabase.Refresh(); } catch (Exception e) { Debug.LogError(e.ToString()); } } [MenuItem("Tools/ExportExcelModels")] private static void ExportModels() { try { string path = string.Format("{0}/{1}", Application.dataPath, excelModelPath); FileInfo[] files = FilesUtil.LoadFiles(path); foreach (var file in files) { //过滤文件 if (file.Extension != ".xlsx") continue; ExcelPackage excelPackage = new ExcelPackage(file); ExcelWorksheets worksheets = excelPackage.Workbook.Worksheets; //只导表1 ExcelWorksheet worksheet = worksheets[1]; ExportJson(worksheet, Path.GetFileNameWithoutExtension(file.FullName), ExporterMode.Model); ExportClass(worksheet, Path.GetFileNameWithoutExtension(file.FullName), ExporterMode.Model); } AssetDatabase.Refresh(); } catch (Exception e) { Debug.LogError(e.ToString()); } } /// /// 导出类 ///
private static void ExportClass(ExcelWorksheet worksheet, string fileName, ExporterMode mode) { string[] properties = GetProperties(worksheet); StringBuilder sb = new StringBuilder(); sb.Append("using System;\t\n"); sb.Append("[Serializable]\t\n"); sb.Append($"public class {fileName}{mode.ToString()} ");//类名 if (mode == ExporterMode.Model)//模型类继承模型接口 sb.Append(": IModel"); sb.Append("\n"); sb.Append("{\n"); for (int col = 1; col <= properties.Length; col++) { string fieldType = GetType(worksheet, col); string fieldName = properties[col - 1]; sb.Append($"\tpublic {fieldType} {fieldName};\n"); } sb.Append("}\n\n"); FilesUtil.SaveFile(string.Format("{0}/{1}", Application.dataPath, mode == ExporterMode.Config ? configClassPath : modelClassPath), string.Format("{0}{1}.cs", fileName, mode.ToString()), sb.ToString()); } /// /// 导出JSON ///
private static void ExportJson(ExcelWorksheet worksheet, string fileName, ExporterMode mode) { string str = ""; int num = 0; string[] properties = GetProperties(worksheet); for (int col = 1; col <= properties.Length; col++) { string[] temp = GetValues(worksheet, col); num = temp.Length; foreach (var value in temp) { str += GetJsonK_VFromKeyAndValues(properties[col - 1], Convert(GetType(worksheet, col), value)) + ','; } } //获取key:value的字符串 str = str.Substring(0, str.Length - 1); str = GetJsonFromJsonK_V(str, num); str = GetUnityJsonFromJson(str); FilesUtil.SaveFile(string.Format("{0}/{1}", Application.dataPath, mode == ExporterMode.Config ? configPath : modelPath), string.Format("{0}{1}.{2}", fileName, mode.ToString(), mode == ExporterMode.Config ? "json" : "record"), str); } /// /// 获取属性 ///
private static string[] GetProperties(ExcelWorksheet worksheet) { string[] properties = new string[worksheet.Dimension.End.Column]; for (int col = 1; col <= worksheet.Dimension.End.Column; col++) { if (worksheet.Cells[propertyIndex, col].Text == "") throw new System.Exception(string.Format("第{0}行第{1}列为空", propertyIndex, col)); properties[col - 1] = worksheet.Cells[propertyIndex, col].Text; } return properties; } /// /// 获取值 ///
private static string[] GetValues(ExcelWorksheet worksheet, int col) { //容量减去前三行 string[] values = new string[worksheet.Dimension.End.Row - 3]; for (int row = valueIndex; row <= worksheet.Dimension.End.Row; row++) { values[row - valueIndex] = worksheet.Cells[row, col].Text; } return values; } /// /// 获取类型 ///
private static string GetType(ExcelWorksheet worksheet, int col) { return worksheet.Cells[typeIndex, col].Text; } /// /// 通过类型返回对应值 ///
private static string Convert(string type, string value) { string res = ""; switch (type) { case "int": res = value; break; case "int32": res = value; break; case "int64": res = value; break; case "long": res = value; break; case "float": res = value; break; case "double": res = value; break; case "string": res = $"\"{value}\""; break; default: throw new Exception($"不支持此类型: {type}"); } return res; } /// /// 返回key:value ///
private static string GetJsonK_VFromKeyAndValues(string key, string value) { return string.Format("\"{0}\":{1}", key, value); } /// ///获取[key:value]转换为{key:value,key:value},再变成[{key:value,key:value},{key:value,key:value}] ///
private static string GetJsonFromJsonK_V(string json, int valueNum) { string str = ""; string[] strs; List listStr = new List(); strs = json.Split(','); listStr.Clear(); for (int j = 0; j < valueNum; j++) { listStr.Add("{" + string.Format("{0},{1}", strs[j], strs[j + valueNum]) + "}"); } str = "["; foreach (var l in listStr) { str += l + ','; } str = str.Substring(0, str.Length - 1); str += ']'; return str; } /// /// 适应JsonUtility.FromJson函数的转换格式 ///
private static string GetUnityJsonFromJson(string json) { return "{" + "\"datas\":" + json + "}"; }}
存档类代码:
存档类
using UnityEngine;using System.IO;using System.Collections.Generic;using System;/// /// 本地模式存档类///
public class Recorder : Singleton{ /// /// 不同模式下的存储路径 ///
private string RecordPath { get {#if (UNITY_EDITOR || UNITY_STANDALONE) return string.Format("{0}/Records", Application.dataPath);#else return string.Format("{0}/Records", Application.persistentDataPath);#endif } } /// /// 用来临时存储存档的容器,便与定时存储而不是每一次修改都进行存储 ///Key是文件名,Value是内容 ///
private Dictionary _cache = new Dictionary(); public Recorder() { _cache.Clear(); FileInfo[] files = FilesUtil.LoadFiles(RecordPath); foreach (var f in files) { string key = Path.GetFileNameWithoutExtension(f.FullName); string value = File.ReadAllText(f.FullName); _cache.Add(key, value); } } /// /// 通常不会修改一次数据就保存一次,间隔保存或者统一保存可以调用此方法 /// 强制手动保存 /// 将cache内容同步到本地文件 ///
public void ForceSave() { FileInfo[] files = FilesUtil.LoadFiles(RecordPath); foreach (var f in files) { string name = Path.GetFileNameWithoutExtension(f.Name); if (_cache.ContainsKey(name)) { string path = string.Format("{0}/{1}.record", RecordPath, name); if (File.Exists(path)) File.Delete(path); //重新写入 File.WriteAllText(path, _cache[name]); } } } /// /// 读取数据,dynamic表示你是从对象的cache中获取数据,还是读取静态存档的数据 ///
public DataList LoadData() where T : IModel { try { string fileContent = _cache[typeof(T).Name]; DataList dataList = JsonUtility.FromJson<DataList>(fileContent); return dataList; } catch (Exception err) { throw new System.Exception(err.ToString()); } } /// /// 存储数据,暂存在字典中或者持续存储到文件中 /// 不建议每次更改数据都存储到文件中 /// 非必要不使用save = true,建议使用ForceSave进行一次性的统一存储 ///
public void SaveData(DataList data, bool save = false) where T : IModel { string json = JsonUtility.ToJson(data); try { _cache[typeof(T).Name] = json; if (save) { string path = string.Format("{0}/{1}.record", RecordPath, typeof(T).Name); if (File.Exists(path)) File.Delete(path); //重新写入 File.WriteAllText(path, json); } } catch (System.Exception) { throw; } } #region CURD public void CreateData(T data, bool save = false) where T : IModel { DataList dataList = LoadData(); dataList.datas.Add(data); SaveData(dataList, save); } public void UpdateData(int index, T data, bool save = false) where T : IModel { try { DataList dataList = LoadData(); dataList.datas[index] = data; SaveData(dataList, save); } catch (Exception err) { throw new System.Exception(err.ToString()); } } public T ReadData(int index) where T : IModel { try { DataList dataList = LoadData(); return dataList.datas[index]; } catch (Exception err) { throw new System.Exception(err.ToString()); } } public void DeleteData(T data, bool save = false) where T : IModel { DataList dataList = LoadData(); dataList.datas.Remove(data); SaveData(dataList, save); } public void DeleteData(int index, bool save = false) where T : IModel { try { DataList dataList = LoadData(); dataList.datas.RemoveAt(index); SaveData(dataList, save); } catch (System.Exception) { throw; } } #endregion}
Config读取代码:
ConfigLoader
using UnityEngine;public class ConfigLoader : Singleton{ public DataList LoadConfig() { string json = Resources.Load("Json/" + typeof(T).Name).text; DataList dataList = JsonUtility.FromJson<DataList>(json); return dataList; }}
本文来自博客园,作者:C1earF,转载请注明原文链接:https://www.cnblogs.com/ameC1earF/p/17271104.html