首先我们明确一下导表工具的目的就是将,策划的表格数据转换为游戏里面程序方便调用的数据格式,一般都是游戏初始换的时候,以字典的结构<Type,<Id,object>> 类似的结构加载表格数据,我们的导表工具就是将Excel表,生成对应的类结构,二进制数据。
1.第一步过滤表格,因为我们的表格是服务器和客户端都放在一起,只是以表头做标志进行区分,如果放在不同的文件夹下的话,就不用做过滤了?下载链接
/// <summary>
/// 导入CSV数据
/// </summary>
private static bool ImportCsvData(FileInfo file)
{
DataTable dt = GetCsvData(file);
string s = dt.Rows[0][0] as string;
if (s != "CS" && s != "C")
{
UnityEngine.Debug.LogError("客户端无需处理不带C的表格");
return false; // 客户端无需处理不带C的表格
}
CreateByteData(file.Name.Replace(file.Extension, ""), dt);
return true;
}
2.第二步,填充数据到DataTable中,这里我用的是.csv表格(excel需要插件进行读取)这个其实就可以看作一个txt文件,中间用逗号隔开,这里我们用正则表达式过滤处理
/// <summary>
/// 可忽略引号中的","
/// </summary>
private static Regex pattern = new Regex(",(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))");
/// <summary>
/// 解析CSV数据到DataTable
/// </summary>
private static DataTable GetCsvData(FileInfo file)
{
DataTable dataTable = new DataTable();
StreamReader streamReader = file.OpenText();
string oneline;
bool bFirstRow = true;
int columnCount = 0;
while ((oneline = streamReader.ReadLine()) != null)
{
string[] itemArray = pattern.Split(oneline);
// 获取列数
if (bFirstRow)
{
foreach (var item in itemArray)
{
DataColumn dataColumn = new DataColumn(string.Format("F{0}", ++columnCount));
dataTable.Columns.Add(dataColumn);
}
bFirstRow = false;
}
//填充数据并加入到datatable中
dataTable.Rows.Add(itemArray.Take(columnCount).ToArray());
}
streamReader.Close();
return dataTable;
}
3.第三步读取数据生产二进制文件与对应表格类,生产二进制文件就是将DataTable结构下的数据写到数据流中最终生成一个txt文件,因为如果要热更的话bundle只支持txt热更(.bytes这种后缀或者无后缀的不支持),生产对于表格类的话就可以下写出个模板来,对应字段名替换就行,其实就当作用程序写一个txt就可以了,只不过后缀是.cs
//表头
/// <summary>
/// 将DataTable数据转换为二进制格式
/// </summary>
private static void CreateByteData(string fileName, DataTable dt)
{
try
{
//数据格式 行数 列数 二维数组每项的值 这里不做判断 都用string存储
string[,] tableHeadArr = null;
byte[] buffer = null;
using (MemoryStream ms = new MemoryStream())
using (BinaryWriter writer = new BinaryWriter(ms))
{
//行数
int rows = dt.Rows.Count;
//列数
int columns = dt.Columns.Count;
tableHeadArr = new string[columns, HeaderRows];
writer.Write(rows - HeaderRows); //减去表头
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < columns; j++)
{
if (i < HeaderRows)
{
tableHeadArr[j, i] = dt.Rows[i][j].ToString().Trim();
}
else
{
if (j > 0 && tableHeadArr[j, CSRow] != "CS" && tableHeadArr[j, CSRow] != "C")
{
continue;
}
string type = tableHeadArr[j, TypeRow];
string value = dt.Rows[i][j].ToString().Trim();
switch (type.ToLower())
{
case "int":
writer.Write(string.IsNullOrWhiteSpace(value) ? 0 : int.Parse(value));
break;
case "long":
writer.Write(string.IsNullOrWhiteSpace(value) ? 0 : long.Parse(value));
break;
case "short":
writer.Write(string.IsNullOrWhiteSpace(value) ? (short)0 : short.Parse(value));
break;
case "float":
writer.Write(string.IsNullOrWhiteSpace(value) ? 0 : float.Parse(value));
break;
case "byte":
writer.Write(string.IsNullOrWhiteSpace(value) ? (byte)0 : byte.Parse(value));
break;
case "bool":
writer.Write(string.IsNullOrWhiteSpace(value) ? false : bool.Parse(value));
break;
case "double":
writer.Write(string.IsNullOrWhiteSpace(value) ? 0 : double.Parse(value));
break;
case "vector3":
try
{
string[] array = value.Trim('\"').Split(',');
float x = float.Parse(array[0]);
float y = float.Parse(array[1]);
float z = float.Parse(array[2]);
writer.Write(x);
writer.Write(y);
writer.Write(z);
}
catch
{
Debug.LogError($"解析Vector3错误! str={value}");
writer.Write(0f);
writer.Write(0f);
writer.Write(0f);
}
break;
case "vector2":
try
{
string[] array = value.Trim('\"').Split(',');
float x = float.Parse(array[0]);
float y = float.Parse(array[1]);
writer.Write(x);
writer.Write(y);
}
catch
{
Debug.LogError($"解析Vector2错误! str={value}");
writer.Write(0f);
writer.Write(0f);
}
break;
default:
writer.Write(value);
break;
}
}
}
}
buffer = ms.GetBuffer();
}
CreateTableTableData(fileName, tableHeadArr, buffer);
CreateTableTableCSharp(fileName, tableHeadArr, buffer);
}
catch (System.Exception ex)
{
Debug.LogError("Excel=>" + fileName + " Generate Failed:" + ex.Message);
}
}
/// <summary>
/// 生成Byte文件
/// </summary>
private static void CreateTableTableData(string fileName, string[,] dataArr, byte[] buffer)
{
string path = string.Format("{0}\\{1}", OutBytesFilePath, fileName + ".txt");
FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write);
BinaryWriter binaryWriter = new BinaryWriter(fs);
binaryWriter.Write(buffer);
binaryWriter.Flush();
binaryWriter.Close();
}
/// <summary>
/// 生成代码
/// </summary>
private static void CreateTableTableCSharp(string fileName, string[,] dataArr, byte[] buffer)
{
string templateTable = File.ReadAllText(Sourcepath + "CSharpTemplate/TemplateTable.txt");
string templateColumn = File.ReadAllText(Sourcepath + "CSharpTemplate/TemplateColumn.txt");
StringBuilder columnSb = new StringBuilder();
StringBuilder ctorSb = new StringBuilder();
for (int i = 0; i < dataArr.GetLength(0); i++)
{
if (i == 0)
continue;
if (dataArr[i, CSRow] != "CS" && dataArr[i, CSRow] != "C")
continue;
columnSb.Append(string.Format(templateColumn, dataArr[i, DescriptorRow], dataArr[i, TypeRow], dataArr[i, NameRow]));
ctorSb.AppendLine($" {dataArr[i, NameRow]} = reader.Read{ChangeTypeNameBinaryReader(dataArr[i, TypeRow])}();");
}
string entityContent = templateTable.Replace("{0}", fileName).Replace("{1}", columnSb.ToString()).Replace("{2}", ctorSb.ToString());
using (FileStream fs = new FileStream(string.Format("{0}/{1}Table.cs", OutCSharpFilePath, fileName), FileMode.Create))
{
using (StreamWriter sw = new StreamWriter(fs))
{
sw.Write(entityContent);
}
}
}
4.最后就是写一个加载的方法,同样是用模板去搞
private static void CreateTableLoadCSharp(List<string> list)
{
StringBuilder sb = new StringBuilder();
string templateLoad = File.ReadAllText(Sourcepath + "CSharpTemplate/LoadData.txt");
for (int i = 0; i < list.Count; i++)
{
sb.Append(templateLoad.Replace("{1}", list[i]));
}
string templateTable = File.ReadAllText(Sourcepath + "CSharpTemplate/DatasLoder.txt");
string str = templateTable.Replace("{1}", sb.ToString());
str = str.Replace("{2}", list.Count.ToString());
using (FileStream fs = new FileStream($"{OutCSharpFilePath}/DatasLoder.cs", FileMode.Create))
{
using (StreamWriter sw = new StreamWriter(fs))
{
sw.Write(str);
}
}
}
5.写一个管理类,用于游戏启动加载表格数据,这个就不介绍了,就是加载数据。
|