1.为编辑器菜单栏添加新的选项入口
通过Unity提供的MenuItem特性在菜单栏添加选项按钮
特性名:MenuItem
命名空间:UnityEditor
要求:一定是静态方法;新建的这个菜单栏按钮 必须有至少一个斜杠 不然会报错 它不支持只有一个菜单栏入口 ;这个特性可以用在任意的类当中
[MenuItem("GameTool/Test")]
private static void Test()
{
Directory.CreateDirectory(Application.dataPath + "/测试文件夹");
AssetDatabase.Refresh();
}
同时,通过以上方式,可以调用后自动刷新窗口
类名:AssetDatabase
命名空间:UnityEditor
方法:Refresh
补充:Editor文件夹
Editor文件夹可以放在项目的任何文件夹下,可以有多个,放在其中的内容,项目打包时不会被打包到项目中。一般编辑器相关代码都可以放在该文件夹中
同时不能再非editor的文件夹下的脚本调用editor文件夹下的脚本
2.导入ExcelDll包
Excel表的本质:Excel表本质上也是一堆数据,有自己的存储读取规则
DLL文件用来解析Excel文件的
Dll文件:库文件,可以理解为它是许多代码的集合,将相关代码集合在库文件中可以方便迁移和使用,方便用户直接使用
(建议放在editor文件夹下)
3.Excel数据读取
利用FileStream读取文件流,再利用IExcelDataReader类,从流中读取Excel数据,使用DataSet数据集合类,将Excel数据转存进其中方便读取
using (FileStream fs = File.Open(Application.dataPath + "/ArtRes/Excel/PlayerInfo.xlsx", FileMode.Open, FileAccess.Read ))
{
//通过文件流获取Excel数据
IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(fs);
//将excel表中的数据转换为DataSet数据类型 方便获取其中的内容
DataSet result = excelReader.AsDataSet();
//得到Excel文件中的所有表信息
for (int i = 0; i < result.Tables.Count; i++)
{
Debug.Log("表名:" + result.Tables[i].TableName);
Debug.Log("行数:" + result.Tables[i].Rows.Count);
Debug.Log("列数:" + result.Tables[i].Columns.Count);
}
fs.Close();
}
获取Excel表中单元格的信息
using (FileStream fs = File.Open(Application.dataPath + "/ArtRes/Excel/PlayerInfo.xlsx", FileMode.Open, FileAccess.Read))
{
IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(fs);
DataSet result = excelReader.AsDataSet();
for (int i = 0; i < result.Tables.Count; i++)
{
DataTable table = result.Tables[i];
DataRow row;
for (int j = 0; j < table.Rows.Count; j++)
{
//得到每一行的信息
row = table.Rows[j];
for (int k = 0; k < table.Columns.Count; k++)
{
Debug.Log(row[k].ToString());
}
}
}
fs.Close();
}
DataTable 数据表类:表示Excel文件中的一个表
DataRow 数据行类:表示某张表中的一行数据
可以根据表中数据来动态的生成相关数据:数据结构类;容器类;2进制数据
为什么不直接读取Excel表而要把它转成2进制数据?
提升读取效率和数据安全性
4.制定配置表的相关规则
数据结构类
容器:放一个字典,存储整张表的数据,对应数据结构类的类型,key利用唯一ID来设置(保证主键不一样)
二进制数据
5.读取Excel目录下所有的excel文件
声明文件存放的路径
public static string EXCEL_PATH = Application.dataPath + "/ArtRes/Excel/";
加载指定路径中的所有Excel文件,用于生成对应的3个文件
DirectoryInfo dInfo = Directory.CreateDirectory(EXCEL_PATH);
//得到指定路径中的所有文件信息 相当于就是得到所有的Excel表
FileInfo[] files = dInfo.GetFiles();
遍历数组中所有的内容,其中会有meta文件,之后操作中无需对其进行处理
for (int i = 0; i < files.Length; i++)
{
if (files[i].Extension != ".xlsx" &&
files[i].Extension != ".xls")
continue;
}
files[i].Extension 记录的是文件的后缀名
设置数据类容器
DataTableCollection tableConllection;
打开一个Excel文件得到其中的所有表的数据
using (FileStream fs = files[i].Open(FileMode.Open, FileAccess.Read))
{
IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(fs);
tableConllection = excelReader.AsDataSet().Tables;
fs.Close();
}
遍历文件中的所有表的信息
foreach (DataTable table in tableConllection)
{
Debug.Log(table.TableName);
}
6.生成数据结构类
创建生成Excel表对应的数据结构类
private static void GenerateExcelDataClass(DataTable table)
{
}
获取变量名所在行
private static DataRow GetVariableNameRow(DataTable table)
{
return table.Rows[0];
}
获取变量类型所在行
private static DataRow GetVariableTypeRow(DataTable table)
{
return table.Rows[1];
}
回到生成Excel表对应的数据结构类
private static void GenerateExcelDataClass(DataTable table)
{
//字段名行
DataRow rowName = GetVariableNameRow(table);
//字段类型行
DataRow rowType = GetVariableTypeRow(table);
}
设置数据结构类脚本存储位置路径
public static string DATA_CLASS_PATH = Application.dataPath + "/Scripts/ExcelData/DataClass/";
生成对应的数据结构类脚本(通过代码进行字符串拼接 然后存进文件)
//判断路径是否存在 没有的话 就创建文件夹
if (!Directory.Exists(DATA_CLASS_PATH))
Directory.CreateDirectory(DATA_CLASS_PATH);
string str = "public class " + table.TableName + "\n{\n";
//变量进行字符串拼接
for (int i = 0; i < table.Columns.Count; i++)
{
str += " public " + rowType[i].ToString() + " " + rowName[i].ToString() + ";\n";
}
str += "}";
把拼接好的字符串存到指定文件中去
File.WriteAllText(DATA_CLASS_PATH + table.TableName + ".cs", str);
最后刷新Project窗口
AssetDatabase.Refresh();
7.生成容器类
利用字典,表示一张表的数据
创建Excel表对应的数据容器类
private static void GenerateExcelContainer(DataTable table)
{
}
获取主键索引
private static int GetKeyIndex(DataTable table)
{
DataRow row = table.Rows[2];
for (int i = 0; i < table.Columns.Count; i++)
{
if (row[i].ToString() == "key")
return i;
}
return 0;
}
获取变量类型所在行
private static DataRow GetVariableTypeRow(DataTable table)
{
return table.Rows[1];
}
得到主键索引
int keyIndex = GetKeyIndex(table);
得到字段类型行
DataRow rowType = GetVariableTypeRow(table);
设置容器类脚本存储位置路径
public static string DATA_CONTAINER_PATH = Application.dataPath + "/Scripts/ExcelData/Container/";
具体容器类内部逻辑
//没有路径创建路径
if (!Directory.Exists(DATA_CONTAINER_PATH))
Directory.CreateDirectory(DATA_CONTAINER_PATH);
string str = "using System.Collections.Generic;\n";
str += "public class " + table.TableName + "Container" + "\n{\n";
str += " ";
str += "public Dictionary<" + rowType[keyIndex].ToString() + ", " + table.TableName + ">";
str += "dataDic = new " + "Dictionary<" + rowType[keyIndex].ToString() + ", " + table.TableName + ">();\n";
str += "}";
File.WriteAllText(DATA_CONTAINER_PATH + table.TableName + "Container.cs", str);
刷新Project窗口
AssetDatabase.Refresh();
8.生成二进制数据
生成excel 2进制数据
private static void GenerateExcelBinary(DataTable table)
{
}
可以利用StreamingAssets在非编辑模式下,是只读状态
2进制数据存储位置路径
public static string DATA_BINARY_PATH = Application.streamingAssetsPath + "/Binary/";
创建一个2进制文件进行写入
FileStream fs = new FileStream(DATA_BINARY_PATH + table.TableName + ".yuan", FileMode.OpenOrCreate, FileAccess.Write)
存储具体的excel对应的2进制信息
存储我们需要写多少行的数据,方便读取
fs.Write(BitConverter.GetBytes(table.Rows.Count - 4), 0, 4);
存储主键的变量名
string keyName = GetVariableNameRow(table)[GetKeyIndex(table)].ToString();
byte[] bytes = Encoding.UTF8.GetBytes(keyName);
存储字符串字节数组的长度
fs.Write(BitConverter.GetBytes(bytes.Length), 0, 4);
存储字符串字节数组
fs.Write(bytes, 0, bytes.Length);
得到类型行 根据类型来决定应该如何写入数据
DataRow rowType = GetVariableTypeRow(table);
遍历所有内容的行 进行2进制的写入
DataRow row;
DataRow rowType = GetVariableTypeRow(table);
for (int i = BEGIN_INDEX; i < table.Rows.Count; i++)
{
//得到一行的数据
row = table.Rows[i];
for (int j = 0; j < table.Columns.Count; j++)
{
switch (rowType[j].ToString())
{
case "int":
fs.Write(BitConverter.GetBytes(int.Parse(row[j].ToString())), 0, 4);
break;
case "float":
fs.Write(BitConverter.GetBytes(float.Parse(row[j].ToString())), 0, 4);
break;
case "bool":
fs.Write(BitConverter.GetBytes(bool.Parse(row[j].ToString())), 0, 1);
break;
case "string":
bytes = Encoding.UTF8.GetBytes(row[j].ToString());
//写入字符串字节数组的长度
fs.Write(BitConverter.GetBytes(bytes.Length), 0, 4);
//写入字符串字节数组
fs.Write(bytes, 0, bytes.Length);
break;
}
}
}
fs.Close();
最后别忘了刷新Project窗口
AssetDatabase.Refresh();
9.Excel数据文件的使用
创建用于存储所有Excel表数据的容器
private Dictionary<string, object> tableDic = new Dictionary<string, object>();
数据存储的位置
private static string SAVE_PATH = Application.persistentDataPath + "/Data/";
读取excel表对应的2进制文件,根据结构体类名进行判断
using (FileStream fs = File.Open(DATA_BINARY_PATH + typeof(K).Name + ".tang", FileMode.Open, FileAccess.Read)){}
记录读取信息
byte[] bytes = new byte[fs.Length];
fs.Read(bytes, 0, bytes.Length);
fs.Close();
//用于记录当前读取了多少字节了
int index = 0;
//读取多少行数据
int count = BitConverter.ToInt32(bytes, index);
index += 4;
//读取主键的名字
int keyNameLength = BitConverter.ToInt32(bytes, index);
index += 4;
string keyName = Encoding.UTF8.GetString(bytes, index, keyNameLength);
index += keyNameLength;
创建容器类对象(通过反射)
Type contaninerType = typeof(T);
object contaninerObj = Activator.CreateInstance(contaninerType);
(Activator根据type创建对象)
得到数据结构类的Type
Type classType = typeof(K);
通过反射来得到数据结构类所有字段的信息
FieldInfo[] infos = classType.GetFields();
读取每一行的信息
for (int i = 0; i < count; i++)
{
//实例化一个数据结构类 对象
object dataObj = Activator.CreateInstance(classType);
foreach (FieldInfo info in infos)
{
if( info.FieldType == typeof(int) )
{
//相当于就是把2进制数据转为int 然后赋值给了对应的字段
info.SetValue(dataObj, BitConverter.ToInt32(bytes, index));
index += 4;
}
else if (info.FieldType == typeof(float))
{
info.SetValue(dataObj, BitConverter.ToSingle(bytes, index));
index += 4;
}
else if (info.FieldType == typeof(bool))
{
info.SetValue(dataObj, BitConverter.ToBoolean(bytes, index));
index += 1;
}
else if (info.FieldType == typeof(string))
{
//读取字符串字节数组的长度
int length = BitConverter.ToInt32(bytes, index);
index += 4;
info.SetValue(dataObj, Encoding.UTF8.GetString(bytes, index, length));
index += length;
}
}
每次读取完一行的数据,便把这个数据添加到容器对象中
object dicObject = contaninerType.GetField("dataDic").GetValue(contaninerObj);
//通过字典对象得到其中的 Add方法
MethodInfo mInfo = dicObject.GetType().GetMethod("Add");
//得到数据结构类对象中 指定主键字段的值
object keyValue = classType.GetField(keyName).GetValue(dataObj);
mInfo.Invoke(dicObject, new object[] { keyValue, dataObj });
把读取完的表记录下来
tableDic.Add(typeof(T).Name, contaninerObj);
fs.Close();
10.工具包导出
书写相关帮助文档
创建测试工程、
BinaryDataMgr.Instance.InitData();
TowerInfoContainer data = BinaryDataMgr.Instance.GetTable<TowerInfoContainer>();