思想
在游戲程序中,玩家的背包、登錄、人物系統都與資料息息相關,無論是一開始就設定好的默認資料,還是可以動態存取的資料,都需要開發人員去管理,
游戲開發程序中,策劃一般通過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<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 {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());
}
/// <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("{0}{1}.{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 = https://www.cnblogs.com/ameC1earF/archive/2023/03/30/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]);
}
}
}
/// <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
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/548637.html
標籤:其他
上一篇:同步和異步的一些事
下一篇:構建基于深度學習神經網路協同過濾模型(NCF)的視頻推薦系統(Python3.10/Tensorflow2.11)
