当前位置: 首页 > news >正文

Unity 编辑器开发 之 Excel导表工具

一个简单的Excel导表工具,可以用来热更数据配置

工具使用:

  1. 执行菜单 SDGSupporter/Excel/1.Excel2Cs 生成c#脚本。
  2. 等待C#类编译完成
  3. 执行菜单 SDGSupporter/Excel/2.Excel2Bytes 生成XmI临时文件,生成bytes二进制表格数据,删除xml临时文件。
  4. 运行场景SampleScene.unity Console面板打印表格内容。

工具代码:

Assets\Editor\Excel2CsBytesTool.cs 文件夹路径可配置在这里Assets\Scripts\BaseTable.cs Excel配置时支持的类型数组

源Excel文件夹:

ExcelData

生成的C#类:

eg: Assets \Scripts \DataTable \weapon.cs

生成的bytes文件:

eg: Resources \DataTable \weapon.bytes

首先准备一个Excel表 填入数据,注意需要放在Edior目录下

然后编写Excel2CsBytesTool.cs脚本

附上完整代码:

using System.IO;
using Excel;
using System.Data;
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using Table;
using System;
using System.Xml;
using System.Xml.Serialization;
using System.Runtime.Serialization.Formatters.Binary;/// <summary>
/// Excel生成bytes和cs工具
/// </summary>
public class Excel2CsBytesTool
{static string ExcelDataPath = Application.dataPath + "/../ExcelData";//源Excel文件夹,xlsx格式static string BytesDataPath = Application.dataPath + "/Resources/DataTable";//生成的bytes文件夹static string CsClassPath = Application.dataPath + "/Scripts/DataTable";//生成的c#脚本文件夹static string XmlDataPath = ExcelDataPath + "/tempXmlData";//生成的xml(临时)文件夹..static string AllCsHead = "all";//序列化结构体的数组类.类名前缀static char ArrayTypeSplitChar = '#';//数组类型值拆分符: int[] 1#2#34 string[] 你好#再见 bool[] true#false ...static bool IsDeleteXmlInFinish = true;//生成bytes后是否删除中间文件xml[MenuItem("SDGSupporter/Excel/Excel2Cs")]static void Excel2Cs(){Init();Excel2CsOrXml(true);}[MenuItem("SDGSupporter/Excel/Excel2Bytes")]static void Excel2Xml2Bytes(){Init();//生成中间文件xmlExcel2CsOrXml(false);//生成bytesWriteBytes();}static void Init(){if (!Directory.Exists(CsClassPath)){Directory.CreateDirectory(CsClassPath);}if (!Directory.Exists(XmlDataPath)){Directory.CreateDirectory(XmlDataPath);}if (!Directory.Exists(BytesDataPath)){Directory.CreateDirectory(BytesDataPath);}}static void WriteCs(string className, string[] names, string[] types, string[] descs){try{StringBuilder stringBuilder = new StringBuilder();stringBuilder.AppendLine("using System;");stringBuilder.AppendLine("using System.Collections.Generic;");stringBuilder.AppendLine("using System.IO;");stringBuilder.AppendLine("using System.Runtime.Serialization.Formatters.Binary;");stringBuilder.AppendLine("using System.Xml.Serialization;");stringBuilder.Append("\n");stringBuilder.AppendLine("namespace Table");stringBuilder.AppendLine("{");stringBuilder.AppendLine("    [Serializable]");stringBuilder.AppendLine("    public class " + className);stringBuilder.AppendLine("    {");for (int i = 0; i < names.Length; i++){stringBuilder.AppendLine("        /// <summary>");stringBuilder.AppendLine("        /// " + descs[i]);stringBuilder.AppendLine("        /// </summary>");stringBuilder.AppendLine("        [XmlAttribute(\"" + names[i] + "\")]");string type = types[i];if (type.Contains("[]")){//type = type.Replace("[]", "");//stringBuilder.AppendLine("        public List<" + type + "> " + names[i] + ";");//可选代码://用_name字段去反序列化,name取_name.item的值,直接返回list<type>。//因为xml每行可能有多个数组字段,这样就多了一层变量item,所以访问的时候需要.item才能取到list<type>//因此用额外的一个变量直接返回List<type>。type = type.Replace("[]", "");stringBuilder.AppendLine("        public List<" + type + "> " + names[i] + "");stringBuilder.AppendLine("        {");stringBuilder.AppendLine("            get");stringBuilder.AppendLine("            {");stringBuilder.AppendLine("                if (_" + names[i] + " != null)");stringBuilder.AppendLine("                {");stringBuilder.AppendLine("                    return _" + names[i] + ".item;");stringBuilder.AppendLine("                }");stringBuilder.AppendLine("                return null;");stringBuilder.AppendLine("            }");stringBuilder.AppendLine("        }");stringBuilder.AppendLine("        [XmlElementAttribute(\"" + names[i] + "\")]");stringBuilder.AppendLine("        public " + type + "Array _" + names[i] + ";");}else{stringBuilder.AppendLine("        public " + type + " " + names[i] + ";");}stringBuilder.Append("\n");}stringBuilder.AppendLine("        public static List<" + className + "> LoadBytes()");stringBuilder.AppendLine("        {");stringBuilder.AppendLine("            string bytesPath = \"" + BytesDataPath + "/" + className + ".bytes\";");stringBuilder.AppendLine("            if (!File.Exists(bytesPath))");stringBuilder.AppendLine("                return null;");stringBuilder.AppendLine("            using (FileStream stream = new FileStream(bytesPath, FileMode.Open))");stringBuilder.AppendLine("            {");stringBuilder.AppendLine("                BinaryFormatter binaryFormatter = new BinaryFormatter();");stringBuilder.AppendLine("                all" + className + " table = binaryFormatter.Deserialize(stream) as all" + className + ";");stringBuilder.AppendLine("                return table." + className + "s;");stringBuilder.AppendLine("            }");stringBuilder.AppendLine("        }");stringBuilder.AppendLine("    }");stringBuilder.Append("\n");stringBuilder.AppendLine("    [Serializable]");stringBuilder.AppendLine("    public class " + AllCsHead + className);stringBuilder.AppendLine("    {");stringBuilder.AppendLine("        public List<" + className + "> " + className + "s;");stringBuilder.AppendLine("    }");stringBuilder.AppendLine("}");string csPath = CsClassPath + "/" + className + ".cs";if (File.Exists(csPath)){File.Delete(csPath);}using (StreamWriter sw = new StreamWriter(csPath)){sw.Write(stringBuilder);Debug.Log("生成:" + csPath);}}catch (System.Exception e){Debug.LogError("写入CS失败:" + e.Message);throw;}}static void WriteXml(string className, string[] names, string[] types, List<string[]> datasList){try{StringBuilder stringBuilder = new StringBuilder();stringBuilder.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>");stringBuilder.AppendLine("<" + AllCsHead + className + ">");stringBuilder.AppendLine("<" + className + "s>");for (int d = 0; d < datasList.Count; d++){stringBuilder.Append("\t<" + className + " ");//单行数据string[] datas = datasList[d];//填充属性节点for (int c = 0; c < datas.Length; c++){string type = types[c];if (!type.Contains("[]")){string name = names[c];string value = datas[c];stringBuilder.Append(name + "=\"" + value + "\"" + (c == datas.Length - 1 ? "" : " "));}}stringBuilder.Append(">\n");//填充子元素节点(数组类型字段)for (int c = 0; c < datas.Length; c++){string type = types[c];if (type.Contains("[]")){string name = names[c];string value = datas[c];string[] values = value.Split(ArrayTypeSplitChar);stringBuilder.AppendLine("\t\t<" + name + ">");for (int v = 0; v < values.Length; v++){stringBuilder.AppendLine("\t\t\t<item>" + values[v] + "</item>");}stringBuilder.AppendLine("\t\t</" + name + ">");}}stringBuilder.AppendLine("\t</" + className + ">");}stringBuilder.AppendLine("</" + className + "s>");stringBuilder.AppendLine("</" + AllCsHead + className + ">");string xmlPath = XmlDataPath + "/" + className + ".xml";if (File.Exists(xmlPath)){File.Delete(xmlPath);}using (StreamWriter sw = new StreamWriter(xmlPath)){sw.Write(stringBuilder);Debug.Log("生成文件:" + xmlPath);}}catch (System.Exception e){Debug.LogError("写入Xml失败:" + e.Message);}}static void Excel2CsOrXml(bool isCs){string[] excelPaths = Directory.GetFiles(ExcelDataPath, "*.xlsx");for (int e = 0; e < excelPaths.Length; e++){//0.读Excelstring className;//类型名string[] names;//字段名string[] types;//字段类型string[] descs;//字段描述List<string[]> datasList;//数据try{string excelPath = excelPaths[e];//excel路径  className = Path.GetFileNameWithoutExtension(excelPath).ToLower();FileStream fileStream = File.Open(excelPath, FileMode.Open, FileAccess.Read);IExcelDataReader excelDataReader = ExcelReaderFactory.CreateOpenXmlReader(fileStream);// 表格数据全部读取到result里DataSet result = excelDataReader.AsDataSet();// 获取表格列数int columns = result.Tables[0].Columns.Count;// 获取表格行数int rows = result.Tables[0].Rows.Count;// 根据行列依次读取表格中的每个数据names = new string[columns];types = new string[columns];descs = new string[columns];datasList = new List<string[]>();for (int r = 0; r < rows; r++){string[] curRowData = new string[columns];for (int c = 0; c < columns; c++){//解析:获取第一个表格中指定行指定列的数据string value = result.Tables[0].Rows[r][c].ToString();//清除前两行的变量名、变量类型 首尾空格if (r < 2){value = value.TrimStart(' ').TrimEnd(' ');}curRowData[c] = value;}//解析:第一行类变量名if (r == 0){names = curRowData;}//解析:第二行类变量类型else if (r == 1){types = curRowData;}//解析:第三行类变量描述else if (r == 2){descs = curRowData;}//解析:第三行开始是数据else{datasList.Add(curRowData);}}}catch (System.Exception exc){Debug.LogError("请关闭Excel:" + exc.Message);return;}if (isCs){//写CsWriteCs(className, names, types, descs);}else{//写XmlWriteXml(className, names, types, datasList);}}AssetDatabase.Refresh();}static void WriteBytes(){string csAssemblyPath = Application.dataPath + "/../Library/ScriptAssemblies/Assembly-CSharp.dll";Assembly assembly = Assembly.LoadFile(csAssemblyPath);if (assembly != null){Type[] types = assembly.GetTypes();for (int i = 0; i < types.Length; i++){Type type = types[i];if (type.Namespace == "Table" && type.Name.Contains(AllCsHead)){string className = type.Name.Replace(AllCsHead, "");//读取xml数据string xmlPath = XmlDataPath + "/" + className + ".xml";if (!File.Exists(xmlPath)){Debug.LogError("Xml文件读取失败:" + xmlPath);continue;}object table;using (Stream reader = new FileStream(xmlPath, FileMode.Open)){//读取xml实例化table: all+classname//object table = assembly.CreateInstance("Table." + type.Name);XmlSerializer xmlSerializer = new XmlSerializer(type);table = xmlSerializer.Deserialize(reader);}//obj序列化二进制string bytesPath = BytesDataPath + "/" + className + ".bytes";if (File.Exists(bytesPath)){File.Delete(bytesPath);}using (FileStream fileStream = new FileStream(bytesPath, FileMode.Create)){BinaryFormatter binaryFormatter = new BinaryFormatter();binaryFormatter.Serialize(fileStream, table);Debug.Log("生成:" + bytesPath);}if (IsDeleteXmlInFinish){File.Delete(xmlPath);Debug.Log("删除:" + bytesPath);}}}}if (IsDeleteXmlInFinish){Directory.Delete(XmlDataPath);Debug.Log("删除:" + XmlDataPath);}}
}

接着是自定义的数据配置类:

using System;
using System.Collections.Generic;
using System.Xml.Serialization;/// <summary>
/// Excel2CsBytesTool
/// Excel可以配置的数组类型:string[] int[] bool[] 
/// 可自行扩展
/// </summary>
namespace Table
{[System.SerializableAttribute()][System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]public class stringArray{[System.Xml.Serialization.XmlElementAttribute("item")]public List<string> item { get; set; }}[System.SerializableAttribute()][System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]public class intArray{[System.Xml.Serialization.XmlElementAttribute("item")]public List<int> item { get; set; }}[System.SerializableAttribute()][System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]public class boolArray{[System.Xml.Serialization.XmlElementAttribute("item")]public List<bool> item { get; set; }}
}

然后就可以在Unity菜单界面看到我们的编辑器工具了

先点击Execl2Cs生成相应的C#脚本(会先清除原先的) 其后点击2Bytes,将数据序列化成二进制

等待执行完成后点击运行。便可以看到我们的consloe界面出现我们在Excel表中配置的数据。

Excel导表工具就完成了。可以自行扩展。

http://www.lryc.cn/news/603540.html

相关文章:

  • 游戏盾从哪些方面保护网站业务?
  • c语言-数据结构-二叉树OJ之子树与二叉树的构建
  • QT项目 -仿QQ音乐的音乐播放器(第三节)
  • 电脑没有声音了怎么恢复 快速解决音频故障
  • 预装Windows 11系统的新电脑怎么跳过联网验机
  • Wndows Docker Desktop-Unexpected WSL error
  • Docker初学者需要了解的几个知识点(三)
  • docker 重新安裝
  • 小杰数据结构(one day)——心若安,便是晴天;心若乱,便是阴天。
  • 数据结构 排序(2)---选择排序
  • RK3568下的进程间广播通信:用C语言构建简单的中心服务器
  • 【WRF工具】服务器中安装编译GrADS
  • 信创国产Linux操作系统汇总:从桌面到服务器,百花齐放
  • 聚铭安全管家平台2.0实战解码 | 安服篇(三):配置保障 自动核查
  • mapbox进阶,mapbox-gl-draw绘图插件扩展,编辑模式支持点、线、面的捕捉
  • Android系统开发 在Android10版本的Framework中添加系统服务
  • Kafka——Kafka控制器
  • Note3: CNN(卷积神经网络)
  • 八股训练营 40 天心得:一场结束,也是一场新的开始
  • OpenCV 学习探秘之四:从角点检测,SIFT/SURF/ORB特征提取,目标检测与识别,Haar级联分类人脸检测,再到机器学习等接口的全面实战应用与解析
  • 【第四章:大模型(LLM)】01.神经网络中的 NLP-(3)文本情感分类实战
  • 嵌入式中间件-uorb解析
  • 基于深度学习的医学图像分析:使用Capsule Networks实现医学图像分类
  • vscode开发微信小程序
  • 01 基于sklearn的机械学习-机械学习的分类、sklearn的安装、sklearn数据集、数据集的划分、特征工程中特征提取与无量纲化
  • (四十三)深度解析领域特定语言(DSL)第七章——语法分析器组合子(Parser Combinators)
  • 传统数据库连接已OUT!飞算JavaAI开启Java开发智能新潮流
  • 【C++算法】78.BFS解决FloodFill算法_算法简介
  • 两数之和(每天刷力扣hot100系列)
  • ubuntu 25.04 自带JS引擎gjs运行GTK with JavaScript 应用