您的位置:首页 > 移动开发 > Unity3D

Unity编辑器扩展:导表工具(2)(通用版)

2019-06-13 18:24 1661 查看

以下均为我个人总结的方案,如果有所不足的地方,请多多包涵

有更好的方案或者有错误之处,敬请留言,谢谢您看了我的博客

二:读取excel数据

首先,需要加入库文件Aspose.Cells 和System.Data库文件

读取excel数据的思路是:将表中所有数据作为文本数据读取,转成DataTable,然后在工具内进行类型转换。

在读取之前,要先定好我们需要的excel表中的格式,基于以后希望能通用到多个项目。所以我定义的格式支持双端,格式如下:

第四行,关键key数量,简单来说,就是索引,是通过多少个关键key找到对应唯一的数据。

例如,目前的大型网络游戏,宠物分成了阶级,在阶级内又分等级,这时候,就是使用2*2,2个关键key来做索引了。

接下来,基于Asopse.Cells,编写一个工具类,用于读取excel表信息。

以后可以基于这个工具类,再进行扩展,做出将数据导出为excel表内容等等功能。

[code]public class AsposeExcelTool {

/// <summary>
/// 从filetoread读入成DataTable
/// </summary>
public static DataTable readExcel (string filetoread, string title)
{
return ReadFromFile(filetoread, title);
}

private static DataTable ReadFromFile(string filename, string title)
{
if (!File.Exists(filename))
{
Debug.LogWarning("no file");
return null;
}
if (filename.IndexOf(".xls") < 0)
{
Debug.LogWarning("no file2");
return null;
}
try
{
using (FileStream file = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
Workbook wb = new Workbook(file);
if (wb == null)
{
Debug.LogWarning("no file3");
return null;
}

Worksheet ws = null;
if (title != null)
ws = wb.Worksheets[title];
if (ws == null)
{
ws = wb.Worksheets[0];
}
if (ws == null)
{
Debug.LogWarning("no file4");
return null;
}

file.Close();

var iter = ws.Cells.GetEnumerator();
while (iter.MoveNext())
{
Cell cell = iter.Current as Cell;
if (cell.Value != null)
{
if (!cell.Value.ToString().Equals(cell.DisplayStringValue))
{
Debug.LogError("内容: " + cell.Value.ToString() + " 显示内容: " + cell.DisplayStringValue);
Debug.LogError("行:" + (cell.Row + 1) + " 列:" + (cell.Column + 1));
throw new Exception(filename + ":读取出现异常(如果是小数异常,建议重启Unity)");
}
}
}
FixColumns(ws);
return FixRows(ws.Cells.ExportDataTableAsString(0, 0, ws.Cells.MaxDataRow + 1, ws.Cells.MaxDataColumn + 1, true));
}
}
catch (Exception e)
{
Debug.LogError("Read Error : " + e);
}
return null;
}

public static void FixColumns(Worksheet ws)
{
Cells cs = ws.Cells;
List<string> cols = new List<string>();
cols.Clear();
string str;
for (int i = 0;i<=cs.MaxDataColumn;i++)
{
str = ReadCell(cs.GetCell(0, i));
if (!str.Equals(""))
{
ws.Cells[0, i].PutValue("");
Debug.LogWarning("A DataColumn named " + str + " already belongs to this DataTable in " + ws.Name);
}
else
{
cols.Add(str);
}
}
}

public static DataTable FixRows(DataTable dt)
{
while (dt.Rows.Count < 7)
{
dt.Rows.Add("");
}
return dt;
}
}

现在数据已经都转换成DataTable,接下来就是对数据进行处理。

还记得第一篇章中提到的函数UpdateClientData,这就是我处理数据的函数。

[code]    /// <summary>
/// 选中文件路径
/// </summary>
private string curFilePath;
/// <summary>
/// 选中文件名字
/// </summary>
private string curFileName;
/// <summary>
/// 选中文件大写名字
/// </summary>
private string curNewName;

private string fileInfo;                                 //excel里面的中文名字
private int keyNum = 0;                                  //key数量
private bool needError = true;
private Dictionary<string, string> dicFileNames;         //excel名、excel里面的中文名字
private Dictionary<int, string> dicColumnNames;          //excel表内每行对应的中文名字
private Dictionary<int, string> dicClientMembers;        //客户端类型对应的属性名
private Dictionary<int, string> dicClientTypes;          //客户端类型
private Dictionary<int, string> dicServerMembers;        //服务端类型对应的属性名
private Dictionary<int, string> dicServerTypes;          //服务端类型
private List<int> index;                                 //列数
private Dictionary<int, Dictionary<int, string>> dicData;//数据字典

private const int EXPORT_TYPE_LUA = 1;
private const int EXPORT_TYPE_JSON = 2;
private const int EXPORT_TYPE_SCRIPT = 3;
private const int EXPORT_TYPE_ASSET = 4;

private Dictionary<int, string> EXPORT_TYPE_STRING = new Dictionary<int, string>();

void OnEnable()
{
EXPORT_TYPE_STRING.Add(EXPORT_TYPE_LUA, ".lua");
EXPORT_TYPE_STRING.Add(EXPORT_TYPE_JSON, ".json");
EXPORT_TYPE_STRING.Add(EXPORT_TYPE_SCRIPT, ".cs");
EXPORT_TYPE_STRING.Add(EXPORT_TYPE_ASSET, ".asset");
}

private bool UpdateClientData(FileInfoData fi,int exportType, bool show = true)
{
//要取回当前的导出文件名
curFilePath = fi.filePath;
curFileName = fi.fileName;
curNewName = fi.fileNewName;

dicColumnNames = new Dictionary<int, string>();
dicClientMembers = new Dictionary<int, string>();
dicClientTypes = new Dictionary<int, string>();
dicData = new Dictionary<int, Dictionary<int, string>>();

dicFileNames = new Dictionary<string, string>();
index = new List<int>();
dicServerMembers = new Dictionary<int, string>();
dicServerTypes = new Dictionary<int, string>();

bool success = ExportExcelToClient(fi, show, exportType);
if(show)
{
if(!success)
{
Debug.LogError("导出" + curFileName + EXPORT_TYPE_STRING[exportType] + " 失败");
if(show)
{
EditorUtility.DisplayDialog("导出" + EXPORT_TYPE_STRING[exportType] + "文件", "导出" + curFileName + EXPORT_TYPE_STRING[exportType] + " 失败\n     失败\n     失败\n", "哦,不");
}
AssetDatabase.Refresh();
}
}
return success;
}

以上就是一些初始化工作,重点部分在ExportExcelToClient函数里。

[code]    private const int CONST_FILE_INFO_ROW_INDEX = 0;       //表名index
private const int CONST_COLUMN_NAMES_ROW_INDEX = 1;    //字段描述index
private const int CONST_KEYS_ROW_INDEX = 2;            //关键Key index
private const int CONST_CLIENT_MEMBERS_ROW_INDEX = 3;  //客户端属性名字index
private const int CONST_CLIENT_TYPES_ROW_INDEX = 4;    //客户端类型index
private const int CONST_SERVER_MEMBERS_ROW_INDEX = 5;  //服务端属性名字index
private const int CONST_SERVER_TYPES_ROW_INDEX = 6;    //服务端属性名字index

private const string CONST_CLIENT_INT_TYPE = "int";
private const string CONST_CLIENT_STRING_TYPE = "string";
private const string CONST_CLIENT_ARRAY_TYPE = "array";

private static List<string> CLIENT_TYPE_LIST = new List<string>()
{
CONST_CLIENT_INT_TYPE,
CONST_CLIENT_STRING_TYPE,
CONST_CLIENT_ARRAY_TYPE,
};

private bool ExportExcelToClient(FileInfoData fi, bool show, int exportType)
{
bool success = true;
bool suc;
DataTable dataTable = AsposeExcelTool.readExcel(curFilePath, curFileName);
if (null == dataTable)
{
success = false;
Debug.LogError(curFilePath + "读取" + curFileName + "表错误,请检查excel的表名字是否与表文件名一直");
return success;
}
if (dataTable.Rows.Count < (CONST_SERVER_TYPES_ROW_INDEX + 1))
{
success = false;
Debug.LogError(curFilePath + "配置信息不够" + (CONST_SERVER_TYPES_ROW_INDEX + 1) + " 行");
return success;
}
try
{
for (int i = 0; i < dataTable.Rows.Count; i++)
{
switch (i)
{
case CONST_FILE_INFO_ROW_INDEX://读取文件内excel名字
fileInfo = dataTable.Rows[CONST_FILE_INFO_ROW_INDEX][dataTable.Columns[0]].ToString();
dicFileNames.Add(curNewName, fileInfo);
continue;
case CONST_COLUMN_NAMES_ROW_INDEX://读取文件内每行中文名字
success = ReadColumnNames(dataTable);
if (!success)
return success;
continue;
case CONST_KEYS_ROW_INDEX://读取key个数
success = ReadKeys(dataTable);
if (!success)
return success;
continue;
case CONST_CLIENT_MEMBERS_ROW_INDEX://读取客户端属性名字
success = ReadClientMembers(dataTable, show, out suc);
if (!success)
return suc;
continue;
case CONST_CLIENT_TYPES_ROW_INDEX://读取客户端属性类型
success = ReadClientTypes(dataTable, show, out suc);
if (!success)
return suc;
continue;
case CONST_SERVER_MEMBERS_ROW_INDEX://读取服务端属性名字

continue;
case CONST_SERVER_TYPES_ROW_INDEX://读取服务端属性类型

continue;
default:
break;
}
//因为前六行已经在上面进行了判断,并且continue之后,跳过了下面的步骤
if (!show && fi.toJson) return true;
Dictionary<int, string> dicRowData = new Dictionary<int, string>();
for (int j = 0; j < dataTable.Columns.Count; j++)
{
dicRowData[j] = dataTable.Rows[i][dataTable.Columns[j]].ToString();
}
dicData[i] = dicRowData;//哪行,对应那一行的所有列数的数据
}

//这里接下去是数据导出部分,将在第三篇章放出。

}

以上处理datatable中的数据,之前定义好的excel格式,处理不同行数的数据。

根据不同行数,不同处理如下:

[code]    #region 客户端读取各个字段Tool
private bool ReadColumnNames(DataTable dataTable)
{
for (int i=0;i<dataTable.Columns.Count;i++)
{
dicColumnNames[i] = dataTable.Rows[CONST_COLUMN_NAMES_ROW_INDEX][dataTable.Columns[0]].ToString();
}
if (dicColumnNames.Count <=0)
{
Debug.LogError(curFilePath + "未找到该字段的说明文字,请把表的单元格式设为文字");
return false;
}
return true;
}

private bool ReadKeys(DataTable dataTable)
{
bool success = true;
string str = dataTable.Rows[CONST_KEYS_ROW_INDEX][dataTable.Columns[0]].ToString().ToLower();
keyNum = 1;
needError = true;
if (str.Equals("1*1"))
{
keyNum = 1;
}
else if(str.Equals("2*2"))
{
keyNum = 2;
}
else if(str.Equals("3*3"))
{
keyNum = 3;
}
if (dataTable.Columns.Count > 1)
{
str = dataTable.Rows[CONST_KEYS_ROW_INDEX][dataTable.Columns[1]].ToString().ToLower();
if (str.Equals("n_error"))
{
needError = false;
}
}
return success;
}

private bool ReadClientMembers(DataTable dataTable,bool show,out bool success)
{
string member;
success = true;
List<string> memberList = new List<string>();
for (int i= 0;i<dataTable.Columns.Count;i++)
{
member = dataTable.Rows[CONST_CLIENT_MEMBERS_ROW_INDEX][dataTable.Columns[i]].ToString();
if (string.IsNullOrEmpty(member)||member.Equals(""))
{
continue;//第几列,空,就跳转,不加入到index中,这样循环的时候,index会找到对应的有数据的列
}
if (memberList.Contains(member))
{
Debug.LogError(curFilePath + "存在表字段重名: " + member);
success = false;
return false;
}
memberList.Add(member);
dicClientMembers[i] = member;
index.Add(i);
}
memberList.Clear();
if (dicClientMembers.Count<=0)
{
if(show)
{
Debug.LogError(curFilePath + "没有表字段配置,若此表要导出客户端数据,请检查字段设置,并把表的单元格式设置为文字");
success = false;
}
return false;
}
return true;

1e92c
}

private bool ReadClientTypes(DataTable dataTable, bool show, out bool success)
{
success = true;
string type;
for (int i=0;i<dataTable.Columns.Count;i++)
{
type = dataTable.Rows[CONST_CLIENT_TYPES_ROW_INDEX][dataTable.Columns[i]].ToString();
if(!CLIENT_TYPE_LIST.Contains(type))
{
if(dicClientMembers.ContainsKey(i))
{
Debug.LogError(curFilePath + "第" + (i + 1) + "列配置了错误的客户端数据类型" + type + ",目前仅支持的类型有" + string.Join(",", CLIENT_TYPE_LIST.ToArray()));
success = false;
return false;
}
continue;
}
else
{
if(!dicClientMembers.ContainsKey(i))
{
Debug.LogError(curFilePath + "第" + (i + 1) + "列没有客户端类型名");
success = false;
return false;
}
}
dicClientTypes[i] = type;
}
if (dicClientTypes.Count<=0)
{
if (show)
{
Debug.LogError(curFilePath + "没有客户端字段类型配置,若此表要导出客户端数据,请检查字段类型配置,并把表的单元格式设置为“文字”");
success = false;
}
return false;
}
return true;
}

#endregion

经过了这些处理之后,定义的格式中的不同行数的数据就已经全部读取并分类好了。

因我个人小项目只是个单机游戏,所以服务端的部分暂时没有编写。

工具都是慢慢完善的,待以后我完善了也会回头重新梳理写过的文章。

接下来就是将数据运用起来,然后导出我们想要的格式,详细将在第三篇章阐述。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: