找回密码
 立即注册
查看: 830|回复: 0

[笔记] Unity配表工具

[复制链接]
发表于 2020-11-29 10:48 | 显示全部楼层 |阅读模式
前言

最近把前同事 @Mike Jinhua 写的配表工具整理了一下,网上的Unity配表工具有很多,也算分享一下另一种实现吧。
工具介绍

生成工具使用python3写的,python写工具有几个优点,首先语言是跨平台的,常用IDE也是跨平台的。然后python是解释性语言,可以直接调试,可以快速测试功能。python的下载链接如下
IDE使用的是pycharm
关于pycharm的配置网上介绍的文章有很多,参考一篇就可以了。最后工具打开界面如下所示
python工具使用了第三方库xlrd, 通过pycharm,可以很方便的导入第三方库
python工具使用的话可能需要设置一下Excel文件目录,生成的二进制文件目录,代码生成目录,在Config.py文件中,下面三个路径需要自定义下,当然也可以使用默认目录。
  1. # Excel文件目录
  2. EXCEL_DIR = "./Excel"
  3. # 数据生成路径
  4. UnityDataDir = "./../ExcelTest/Assets/StreamingAssts/Config/"
  5. # 代码生成路径
  6. UnityCodeDir = "./../ExcelTest/Assets/Scripts/Config/"       
复制代码
工具会在Unity中生成一个二进制数据文件和相应的所有配表类。如下图所示
外部调用的话,只要几行代码就可以初始化所有的表格数据。
  1. string configpath = Application.streamingAssetsPath + "/Config/Config.data";
  2. ConfigManager.LoadConfig(configpath);
复制代码
这个配表工具能做到自动化,数据的配置可以在Excel内完成。程序不需要再写其他代码,只要通过生成的配置类读取相应的数据就可以了。
既然在Excel内就能配置数据,那么把视角转向Excel表吧。打开某一张配置表,如下图所示
每个数据表的表头有5行
    第一行是生成的代码的注释第二行有三个字段可以选择CS,C,S, 分别代表这个字段是客户端服务器通用,只是客户端使用,还是只能是服务器使用。通过这个字段筛选,可以避免生成数据的冗余第三行是生成的字段类型,目前列举了N种类型
int         整型
float      浮点型
bool      bool值
string     字符串
list[int]       整型列表
list[float]        浮点数列表
list[string]      字符串列表
map[int|int]        int为key, int为值的字典
map[int|float]      int为key, float为值的字典
map[int|string]       int为key, string为值的字典
map[string|int]          string为key, int为值的字典
map[string|float]        string为key, float为值的字典
map[string|string]       string为key, string为值的字典
通过选择这些类型,可以生成常用的数据类型
    第四行是生成的字段名字(避免重名)第五个字段是设置生成的配表类有几个Key值
没有Key的情况下,生成的类没有读取函数,只能用List遍历所有的数据
有一个Key的情况下,生成的类数据用Dictionary来管理,可以通过这个Key值获取数据
在有多个Key的情况下,生成的类数据用List来管理,可以通过多个Key值的获取函数获取表格数据
打上Key关键字的字段一般使用int, string这两个基本类型
需要特殊说明的是,在Excel中,对于数组类型字段,需要使用默认的分隔符
对于List字段,分隔符是使用的 ';' (分号)
对于Map字段,一级分隔符是 '|' (竖斜杠), 二级分隔符是 ';' (分号)
具体代码实现

1. Excel2Unity表格生成工具
对于这个python写的表格工具,主要的用来生成二进制数据和代码文本。
A. 二进制文件生成
笔者在整理代码的过程中,对于python生成二进制文件不了解,通过Google学习了python对二进制流的操作
python通过struct类对二进制数据进行读写, 文档如下
在这篇文档中,主要是要确定对于不同的数据写入需要什么格式,主要是下面这张表
目前这个表格工具其实只用到了4种类型int, float, bool, string
在python3中,写入数据使用如下代码 struct.pack(format, val)
    对于int类型,使用的Format是"i", 流入的数据占用4个字节, 在python中代码如下bytes = struct.pack(format, int(val))对于float类型,和int类型相似,使用的Format是"f", 流入的数据占用4个字节, 在python中代码如下 bytes = struct.pack(format, float(val))对于bool类型,使用的Format是"?", 流入的数据占用1个字节,在python中代码如下 bytes = struct.pack(format, bool(val))对于特殊的string类型,根据表格显示,使用的Format是"s", 流入的数据只占用1个字节. 这里就有需要注意的地方了。在配表列出的数据类型中,除了列出的int, float, bool 三种类型,其他的类型流化成二进制数据的时候全部使用的是string流入。看看如下的python代码
  1.             newval = val.encode()
  2.             vallen = len(newval)
  3.             lenbyte = struct.pack("i", vallen)
  4.             strformat = str(vallen) + format
  5.             valbyte = struct.pack(strformat, newval)
  6.             bytes = lenbyte + valbyte
复制代码
上面的代码中,对于Excel表格中读取的单元格数据,首先得到这块数据的所占字节数,然后用int类型流入这个字节数,然后通过字节数和"s"拼装成最终的字符串格式,例如,字节数是20个,那么最终的字符串格式就是"20s", 然后再用struct.pack函数流入二进制数据。
也就是说,对于一个string类型,流入的二进制数据包括占用4个字节的数据长度,以及这么多数据长度的字节。由两部分组成。
完整的python函数如下所示
  1.     @staticmethod
  2.     def Encode2Bytes(format, val):
  3.         if format == "i":
  4.             bytes = struct.pack(format, int(val))
  5.         elif format == "f":
  6.             bytes = struct.pack(format, float(val))
  7.         elif format == "?":
  8.             bytes = struct.pack(format, bool(val))
  9.         elif format == "s":
  10.             newval = val.encode()
  11.             vallen = len(newval)
  12.             lenbyte = struct.pack("i", vallen)
  13.             strformat = str(vallen) + format
  14.             valbyte = struct.pack(strformat, newval)
  15.             bytes = lenbyte + valbyte
  16.             
  17.         return bytes
复制代码
由于只生成一个二进制文件,所以所有文件的所有数据都会拼接起来,保存在一个二进制文件里面。
B. 代码生成
对于代码的生成,就是普通的字符串拼接,然后保存在文件中。需要注意的是:
  1.         file = open(path, "wb")
  2.         file.write(filecontent.encode())
  3.         file.close()
复制代码
对于生成的字符串filecontent, 需要使用encode()函数编码成utf-8格式,encode的默认参数就是编码成utf-8格式
2. Unity数据读取
在Unity中读取生成的二进制文件,需要准备几个基础类,拿出一个生成的配表文件类
  1. public class Test1Cfg
  2. {
  3.     public int ID;    //                int类型
  4.     public float HP;    //                float类型
  5.     public bool HasUse;    //                bool类型
  6.     public string Name1;    //                string类型
  7.     public string Name2;    //                string类型
  8.     public List<int> Vec1;    //                int数组
  9.     public List<float> Vec2;    //                float数组
  10.     public List<string> Vec4;    //                string数组
  11.     public Dictionary<int, int> Map1;    //                intint字典
  12.     public Dictionary<int, float> Map2;    //                intfloat字典
  13.     public Dictionary<int, string> Map4;    //                intstring字典
  14.     public Dictionary<string, int> Map5;    //                stringint字典
  15.     public Dictionary<string, float> Map6;    //                stringfloat字典
  16.     public Dictionary<string, string> Map8;    //                stringstring字典
  17.     public void Deserialize (DynamicPacket packet)
  18.     {
  19.         ID = packet.PackReadInt32();
  20.         HP = packet.PackReadFloat();
  21.         HasUse = packet.PackReadBoolean();
  22.         Name1 = packet.PackReadString();
  23.         Name2 = packet.PackReadString();
  24.         Vec1 = SheetGenCommonFunc.GetListInt(packet.PackReadString());
  25.         Vec2 = SheetGenCommonFunc.GetListFloat(packet.PackReadString());
  26.         Vec4 = SheetGenCommonFunc.GetListString(packet.PackReadString());
  27.         Map1 = SheetGenCommonFunc.GetDictIntInt(packet.PackReadString());
  28.         Map2 = SheetGenCommonFunc.GetDictIntFloat(packet.PackReadString());
  29.         Map4 = SheetGenCommonFunc.GetDictIntString(packet.PackReadString());
  30.         Map5 = SheetGenCommonFunc.GetDictStringInt(packet.PackReadString());
  31.         Map6 = SheetGenCommonFunc.GetDictStringFloat(packet.PackReadString());
  32.         Map8 = SheetGenCommonFunc.GetDictStringString(packet.PackReadString());
  33.     }
  34. }
复制代码
对于python的二进制写入,C#这边的二进制读取分别
使用DynamicPacket的PackReadInt32()函数读取int数据
使用DynamicPacket的PackReadFloat()函数读取float数据
使用DynamicPacket的PackReadBoolean()函数读取boolean数据
使用DynamicPacket的PackReadString()函数读取string数据。
由于string数据流入比较特殊,所以特别看下PackReadString()这个函数的实现
  1.     public string PackReadString()
  2.     {
  3.         int count = this.PackReadInt32();
  4.         if (count == 0)
  5.             return "";
  6.         byte[] bytes = this.PackReadBytes(count);
  7.         return Encoding.UTF8.GetString(bytes);
  8.     }
  9.     public int PackReadInt32()
  10.     {
  11.         return this.mReader.ReadInt32();
  12.     }
  13.     public byte[] PackReadBytes(int count)
  14.     {
  15.         byte[] array = new byte[count];
  16.         for (int i = 0; i < count; i++)
  17.         {
  18.             array[i] = this.mReader.ReadByte();
  19.         }
  20.         return array;
  21.     }
复制代码
首先调用PackReadInt32()函数读取字节数,然后调用PackReadBytes()函数读取出二进制数据流,通过Encoding.UTF8.GetString(bytes)函数获取utf-8格式的string数据。注意到mReader变量,所有的数据都是通过这个变量来读取的,这个变量是BinaryReader类定义的。
对于DynamicPacket这个类的初始化,如下所示
  1.     public BinaryWriter mWriter;
  2.     public BinaryReader mReader;
  3.     public MemoryStream mMemStream;
  4.     public DynamicPacket(byte[] bytes)
  5.         : this()
  6.     {
  7.         this.mMemStream.Write(bytes, 0, bytes.Length);
  8.         this.mMemStream.Seek(0L, SeekOrigin.Begin);
  9.     }
  10.     public DynamicPacket()
  11.     {
  12.         this.mMemStream = new MemoryStream();
  13.         this.mReader = new BinaryReader(this.mMemStream);
  14.         this.mWriter = new BinaryWriter(this.mMemStream);
  15.     }
复制代码
DynamicPacket把表格二进制文件读取,通过BinaryReader来读取所有的二进制数据。通过这个DynamicPacket类,可以流化出所有的数据。
C#进行二进制数据读取不复杂,具体可以看下github的工程代码,最后给出这个工具的github地址。
项目github地址

其他的导表工具实现

github成熟工具

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2025-1-31 23:55 , Processed in 0.108404 second(s), 29 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表