Unity 中的存档系统(本地存档)
Posted ameC1earF
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity 中的存档系统(本地存档)相关的知识,希望对你有一定的参考价值。
思想
在游戏过程中,玩家的背包、登录、人物系统都与数据息息相关,无论是一开始就设定好的默认数据,还是可以动态存取的数据,都需要开发人员去管理。
游戏开发过程中,策划一般通过Excel表格配置一些内容来对游戏的一些行为经行数据的设定。表格有config默认数据,程序只需要读取即可;还可能建立model类数据需要在游戏中实例化对象来进行数据的增删改查.
MVC架构中Model的CRUD操作也包含在存档类中(本地存档):
方法
excel转换成config默认数据(json文件)并通过对应的类读取数据可以参考我之前发的文章
以下我对它进行了改良,涵盖了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<T>
public List<T> datas = new List<T>();
导出类代码:
导出工具类
using UnityEngine;
using UnityEditor;
using System.IO;
using OfficeOpenXml;
using System.Collections.Generic;
using System;
using System.Text;
/// <summary>
/// 导出模式
/// </summary>
public enum ExporterMode
/// <summary>
/// 表格数据,策划配置的默认数据
/// </summary>
Config,
/// <summary>
/// 模型数据,服务器或者本地可以修改的数据
/// </summary>
Model,
/// <summary>
/// 使用EPPlus获取表格数据,同时导出对应的Json以及Class.
/// </summary>
public class ExcelExporter
/// <summary>
/// ExcelConfig路径
/// </summary>
private const string excelConfigPath = "../Assets/Excels/Configs";
/// <summary>
/// ExcelModel路径
/// </summary>
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";
/// <summary>
/// 属性行
/// </summary>
private const int propertyIndex = 2;
/// <summary>
/// 类型行
/// </summary>
private const int typeIndex = 3;
/// <summary>
/// 值行
/// </summary>
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());
/// <summary>
/// 导出类
/// </summary>
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 fileNamemode.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("01.cs", fileName, mode.ToString()), sb.ToString());
/// <summary>
/// 导出JSON
/// </summary>
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("01.2", fileName, mode.ToString(), mode == ExporterMode.Config ? "json" : "record"),
str);
/// <summary>
/// 获取属性
/// </summary>
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;
/// <summary>
/// 获取值
/// </summary>
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;
/// <summary>
/// 获取类型
/// </summary>
private static string GetType(ExcelWorksheet worksheet, int col)
return worksheet.Cells[typeIndex, col].Text;
/// <summary>
/// 通过类型返回对应值
/// </summary>
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;
/// <summary>
/// 返回key:value
/// </summary>
private static string GetJsonK_VFromKeyAndValues(string key, string value)
return string.Format("\\"0\\":1", key, value);
/// <summary>
///获取[key:value]转换为key:value,key:value,再变成[key:value,key:value,key:value,key:value]
/// </summary>
private static string GetJsonFromJsonK_V(string json, int valueNum)
string str = "";
string[] strs;
List<string> listStr = new List<string>();
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;
/// <summary>
/// 适应JsonUtility.FromJson函数的转换格式
/// </summary>
private static string GetUnityJsonFromJson(string json)
return "" + "\\"datas\\":" + json + "";
存档类代码:
存档类
using UnityEngine;
using System.IO;
using System.Collections.Generic;
using System;
/// <summary>
/// 本地模式存档类
/// </summary>
public class Recorder : Singleton<Recorder>
/// <summary>
/// 不同模式下的存储路径
/// </summary>
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
/// <summary>
/// 用来临时存储存档的容器,便与定时存储而不是每一次修改都进行存储
///Key是文件名,Value是内容
/// </summary>
private Dictionary<string, string> _cache = new Dictionary<string, string>();
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);
/// <summary>
/// 通常不会修改一次数据就保存一次,间隔保存或者统一保存可以调用此方法
/// 强制手动保存
/// 将cache内容同步到本地文件
/// </summary>
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]);
/// <summary>
/// 读取数据,dynamic表示你是从对象的cache中获取数据,还是读取静态存档的数据
/// </summary>
public DataList<T> LoadData<T>() where T : IModel
try
string fileContent = _cache[typeof(T).Name];
DataList<T> dataList = JsonUtility.FromJson<DataList<T>>(fileContent);
return dataList;
catch (Exception err)
throw new System.Exception(err.ToString());
/// <summary>
/// 存储数据,暂存在字典中或者持续存储到文件中
/// 不建议每次更改数据都存储到文件中
/// 非必要不使用save = true,建议使用ForceSave进行一次性的统一存储
/// </summary>
public void SaveData<T>(DataList<T> 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>(T data, bool save = false) where T : IModel
DataList<T> dataList = LoadData<T>();
dataList.datas.Add(data);
SaveData<T>(dataList, save);
public void UpdateData<T>(int index, T data, bool save = false) where T : IModel
try
DataList<T> dataList = LoadData<T>();
dataList.datas[index] = data;
SaveData<T>(dataList, save);
catch (Exception err)
throw new System.Exception(err.ToString());
public T ReadData<T>(int index) where T : IModel
try
DataList<T> dataList = LoadData<T>();
return dataList.datas[index];
catch (Exception err)
throw new System.Exception(err.ToString());
public void DeleteData<T>(T data, bool save = false) where T : IModel
DataList<T> dataList = LoadData<T>();
dataList.datas.Remove(data);
SaveData<T>(dataList, save);
public void DeleteData<T>(int index, bool save = false) where T : IModel
try
DataList<T> dataList = LoadData<T>();
dataList.datas.RemoveAt(index);
SaveData<T>(dataList, save);
catch (System.Exception)
throw;
#endregion
Config读取代码:
ConfigLoader
using UnityEngine;
public class ConfigLoader : Singleton<ConfigLoader>
public DataList<T> LoadConfig<T>()
string json = Resources.Load<TextAsset>("Json/" + typeof(T).Name).text;
DataList<T> dataList = JsonUtility.FromJson<DataList<T>>(json);
return dataList;
本文来自博客园,作者:C1earF,转载请注明原文链接:https://www.cnblogs.com/ameC1earF/p/17271104.html
以上是关于Unity 中的存档系统(本地存档)的主要内容,如果未能解决你的问题,请参考以下文章
Unity存档系统丨PlayerPrefs简介,及构建一个基础的数据存储系统