[游戏开发]Python打表工具处理Excel数据并生成proto和bytes [第二篇] 打表流程

[游戏开发]Python打表工具处理Excel数据并生成proto和bytes [第二篇] 打表流程,第1张

概述下面正式开始打表流程(几步走)策划配表习惯使用excel,我们打表目标也是xlsm和xlsx文件开始打表前需要确认好打表工具目录在哪,可以在配表文件夹内新建个文件夹命名TableCreaterTableCreater文件夹下有几个目录要区分清楚,首先是python脚本文件夹叫Scripts,还有在运行工具期间生成的 下面正式开始打表流程(几步走)

策划配表习惯使用excel,我们打表目标也是xlsm和xlsx文件

开始打表前需要确认好打表工具目录在哪,可以在配表文件夹内新建个文件夹命名tableCreater

tableCreater文件夹下有几个目录要区分清楚,首先是python脚本文件夹叫Scripts,还有在运行工具期间生成的各种文件,用Temp文件夹存储,Temp里有PB_Python、PB_lua、Proto、Bytes等文件夹,之后这些文件夹内的文件使用完后要清掉

Scripts文件夹内要有start.py、excelToCSV.py、export_proto.py、export_pb_lua.py、export_pb_python.py、export_bytes.py、export_bytestables.py.

Scripts文件夹内的文件一看就是用来打表的各个流程啦。我们可以在start.py的main函数中按步骤执行即可

[第一步]:将excel文件转CSV并输出到CSV目录

start.py执行excelToCSV.excute()

为何要把excel转csv,excel文件包含了windows的很多库,文件特别大,但csv文件是纯文本文件, 每行数据用逗号','分隔,存储空间小。

def execute(excelname):        xlrd.Book.enCoding = "gbk"        excelPath = setting.dirPath_excel + excelname    excel = xlrd.open_workbook(excelPath) #打开Excel    for sheet_name in excel.sheet_names():   #遍历子表,一个excel可能有多个sheet页        sheet = excel.sheet_by_name(sheet_name)#拿到sheet数据        #一般来说一个excel有多个页签,每一个页签对应一个proto文件,我的表名称写在第二行第一列        tablename = sheet.cell_value(1,0)        csvname = tablename        maxCol = 0 #该sheet页最大列数        for i in range(100): #获取当前sheet的最大列数            if sheet.cell(3,i).ctype == 0:                maxCol = i                break        filePath = setting.dirPath_csv + csvname + ".csv"        fileObj = codecs.open(filePath,"wb")#该代码会在目标目录创建文件,并设置读写格式        fileObj.seek(0)#从第0行开始写        csv_writer = unicodecsv.writer(fileObj,enCoding='utf-8-sig')#unicodecsv是外部库,自行下载导入        tableUser = gettableUser(sheet,endCol) #记录配表使用者,如果没有需求可以不记录,我的项目是服务器和客户端共用表,在数据名称的上一行记录该数据由c还是s使用        csv_writer.writerow([tableUser,excelname.decode("gbk"),sheet_name])#写在第一行        rowCount = sheet.nrows #当前sheet最大行数        for i in range(7,rowCount):#正式开始遍历excel每个子表每行数据            #每个子表每列数据            for j in range(0,endCol):#遍历该行每一列数据                                #此处把读取数据代码省略,读格子数据的代码为sheet.cell(rowx,colx).value                #把你读出来的数据拼接成正确的行            csv_writer.writerow(rowDatas) #按行写入        fileObj.close()        #到此一个完整的XXX.csv文件生成成功,可以用excel打开查看该文件数据,提示:打开的数据必须和excel是一个格子一个格子的,行数据不能在一个格子里
[第二步]:读取csv的数据类型行和数据名称行,使用这两行数据生成proto

start.py执行export_proto.excute()

    def execute(csvname):                    curCsvPath = setting.dirPath_csv + csvname + ".csv"        csvfile = codecs.open(curCsvPath,"rb")        csv_reader = unicodecsv.reader(csvfile,enCoding='utf-8-sig')        index = 0        types = []        Titles = []        for line in csv_reader:            if(index == 0):                canExport,msg = Parser.CanExport(line[0])                if(not canExport):                    deBUG.throwError("导出proto文件失败:{}.csv{}".format(csvname,msg))            elif(index == 1):                userSigns = line  #使用者标识            elif(index == 2): #类型                for i in range(0,len(line)):                    if(Parser.CanUse(userSigns[i])):                        types.append(line[i])            elif(index == 3): #字段名称                for i in range(0,len(line)):                    if(Parser.CanUse(userSigns[i])):                        Titles.append(line[i])            else:                break            index = index + 1        csvfile.close()            #code_block,code_struct,code_import = createcode_block(types,Titles)        code_block,code_struct = createcode_block(types,Titles)        code = m_code_template.format(code_struct,csvname,code_block,csvname,csvname)        codefile = codecs.open(setting.dirPath_proto + "table_" + csvname + ".proto","wb","utf-8")        codefile.write(code)        codefile.close()

生成的文件在Temp/Proto文件夹中

[第三步]:调用protoc-gen-lua.bat生成lua版pb文件

start.py执行export_pb_lua.excute(protoname)

调用protoc-gen-lua.bat并传入参数,

其实就是传入各种路径参数,以及 [第二步] 通过数据类型和数据名称构建的table_XXX.proto 文件路径

lua版pb文件的输出路径等,最后ret如果为true代表生成成功。

最终输出路径我写的是Temp/PB_Lua,文件名为table_xxx_pb.lua

这个lua文件就是可以直接在Unity工程中使用的lua版pb,在工具的最后只需要把Temp/PB_Lua文件夹的文件全部copy到工程中即可。

        cmd = "{}\protoc-gen-lua.bat {} {} {} {}\table_{}.proto".format(            os.path.abspath(setting.protocPath_lua),            os.path.abspath(setting.dirPath_proto),            os.path.abspath(setting.dirPath_protoEnum),            os.path.abspath(setting.dirPath_pb_lua),            os.path.abspath(setting.dirPath_proto),            protoname)        ret,msg = deBUG.system(cmd)        if(not ret):            deBUG.throwError("{}生成python版pb失败=>\n{}".format(protoname,msg))

目前来看,这些路径的传入顺序必须固定,例如proto路径以及想要输出lua版本pb的路径,想要修改的话去改protoc-gen-lua工具的源码

[第四步]:调用protoc.exe生成python版pb文件

start.py执行export_pb_python.excute(protoname)

调用protoc.exe 并传入参数

和上一步有点相似,重点参数还是  [第二步] 生成的proto文件的路径

生成的文件名称为table_xxx_py2.py,我的工程输出目录为Temp/PB_Python,目前protoc.exe只能输出python、JAVA和C++三种格式的pb文件,想生成C#等其他格式需要去网上找工具,使用方式和lua版本的一样

        cmd = "{}\protoc.exe --proto_path={} --python_out={} --proto_path={} {}\table_{}.proto".format(            os.path.abspath(setting.protocPath),            os.path.abspath(setting.dirPath_proto),            os.path.abspath(setting.dirPath_protoEnum),            os.path.abspath(setting.dirPath_pb_python),            os.path.abspath(setting.dirPath_proto),            protoname)           ret,msg = deBUG.system(cmd)        if(not ret):            deBUG.throwError("{}生成python版pb失败=>\n{}".format(protoname,msg))

 

[第五步]:csv生成bytes文件导入Unity工程等待读数据

这一步过程比较复杂,代码中涉及到多次数据构建,先简单说思路

我们提前把这个python文件的结构当做str写好,这个python文件是个模板,也就是下面模块一代码

我们在模块二的逻辑代码中遍历csv的行数据,把行数据处理成一个str数据后塞入到这个模块一的python代码中,并把这段代码生成一个py文件等待第六步,运行生成的py文件

#模块一!!字符串m_code_template 是一段python文件代码,里面缺失了一些文件数据,比如文件路径、文件名称等,补全信息并且直接调用python.exe执行这段代码即可生成bytes文件m_code_template = u'''#! python2#Coding:utf-8import osimport tracebackimport sys    reload(sys)  sys.setdefaultencoding('utf8')sys.path.insert(0, os.path.abspath('./tableCreater/Script'))import Parserimport settingimport deBUGsys.path.insert(0, os.path.abspath(setting.dirPath_pb_pythonEnum))sys.path.insert(0, os.path.abspath(setting.dirPath_pb_python))import table_{}_pb2def export():	tableData = table_{}_pb2.table_{}()	BYTES_PATH = {} +"{}.bytes"{}	bytes = tableData.SerializetoString()	NEWfile = open(BYTES_PATH,"wb")	NEWfile.write(bytes)	NEWfile.close()try:	export()except:	deBUG.error(traceback.format_exc())'''
下面是模块二代码
        #模块二 下面这段代码是补全 m_code_template的信息,并生成一个用于转bytes的python文件                for line in csv_reader:				index = index + 1				if(index == 0):					user = line[0]				elif(index == 1):					userSigns = line				elif(index == 2):					types = line				elif(index == 3):					Titles = line				else:					code_row = createcode_row(line,types,Titles,userSigns,tablename)					allRowStrs.append(code_row)					allRowStrs.append("\n")				# 	parser_code = parser_code + code_row + "\n"				# print(index)						#print("这里")			parser_code = ''.join(allRowStrs)			exportPath = "setting.dirPath_byte"			if user == "clIEnt":				exportPath = "setting.dirPath_byte_clIEntOnly"            code = m_code_template.format(tablename,tablename,tablename,exportPath,tablename,parser_code)			csvfile.close()            c    odefile = codecs.open(setting.dirPath_code + "table_" + tablename + ".py","wb","utf-8-sig") #创建一个python文件,并把数据写入到该python文件中            codefile.write(code)            codefile.close()

下面把模块二中数据构建的函数

def createcode_row(line,types,Titles,userSigns,tablename):	code = u"\tdata = tableData.datas.add()\n"	for i in range(0,len(line)):		if(Parser.CanUse(userSigns[i])):			code = code + createcode_col(line[i],types[i],Parser.GetTitle(Titles[i]),tablename)	return code

贴代码有点不方便用户阅读,先这么整吧

def createcode_col(cell,dataType,Title,tablename):	code = u""	if(cell != u""):		if(Parser.IsArray(dataType)):			if(Parser.IsstructArray(dataType)):				#print("结构体数组")				code = code + createcode_structArray(cell,dataType,Title)			else:				#print("基本数据类型数组")				code = code + createcode_baseTypeArray(cell,dataType,Title)		else:			if(Parser.IsstructArray(dataType)):				code = code + createcode_struct(cell,dataType,Title,tablename)			else:				if dataType == u'JsON':					dataType = u'STRING'				if dataType == u'STRING':					cell = cell.replace('\"','\"')				#基本数据类型赋值				code = code + u"\tdata.{} = Parser.GetValue(u\"{}\",u\"\"\"{}\"\"\")\n".format(Title,dataType,cell)	return code
[第六步]:生成Lua读表脚本,业务开发调用该脚本读数据

[第五步]负责把csv数据各种拼凑组合到一个python模板中,然后生成py文件,第六步就轮到调用该py文件啦

#第五步中生成的python文件是可执行的,使用cmd命令执行该python文件即可生成对应的bytes文件cmd = "\"tableCreater\Python27\python\" {}table_{}.py".format(setting.dirPath_code,codename)        os.system(cmd)

这一步会生成我们想要的bytes文件并输出到Temp/Bytes文件夹中,文件名字为xxx.bytes,这个xxx就是我们前面的csvname或protoname

[第七步]:生成统一的读表接口

使用python全自动生成一个lua文件用来管理所有的读表功能,因为一个工程的表实在太多了。手写太繁琐,该工具可以叫Alltables.lua,当系统启动时,调用该统一入口,把所有表全部加载入内存,统一管理

#此处只举两个表为例,分别是AchIEvement成就表和table_ItemInfo道具表,Alltables.lua代码全部由python批量生成,不是手写的,无论有多少个表都可以循环写入,业务开发的人如果想读成就表,则只需要调用table_AchIEvement.GetRowData(keyname)即可,之后根据框架设计走同步或者异步加载bytes文件并读取行数据,首次加载肯定是要先把bytes数据按Keyname已key、value的方式存下来方便读取

_G.table_AchIEvement = {    Belong = "common",    Keyname = "conditionSID",    InitModule = function ()        require "Logic/table/autoGen/tablePb/table_AchIEvement_pb"        tableCtrl.Loadtable("AchIEvement")    end,    Parser_table = function (bytes)        local table = table_AchIEvement_pb.table_AchIEvement()        table:ParseFromString(bytes);        return table.datas    end,    GetRowData = function (ID)        return tableCtrl.GetRowData("AchIEvement",ID)    end,    GetAllRowData = function ()        return tableCtrl.GetAllRowData("AchIEvement")    end,}_G.table_ItemInfo = {    Belong = "common",    Keyname = "ID",    InitModule = function ()        require "Logic/table/autoGen/tablePb/table_ItemInfo_pb"        tableCtrl.Loadtable("ItemInfo")    end,    Parser_table = function (bytes)        local table = table_ItemInfo_pb.table_ItemInfo()        table:ParseFromString(bytes);        return table.datas    end,    GetRowData = function (ID)        return tableCtrl.GetRowData("ItemInfo",ID)    end,    GetAllRowData = function ()        return tableCtrl.GetAllRowData("ItemInfo")    end,}#当系统启动时,调用Alltables的RegisterModulelocal Alltables = {        Init = function (RegisterModule) #这个是系统注册模块的地方        RegisterModule(table_AchIEvement,false)        RegisterModule(table_ItemInfo,false)}
[额外知识]

我们写好的python工程是可运行的,但其他开发人员不希望打开工程去运行,因此我们写一个exportToClIEnt.cmd文件执行写好的python工程

@echo off echo "cd /d %~dp0的作用是切换到当前目录"cd /d %~dp0"./export_fight_proto/Python27/python" "./export_fight_proto/start.py":end

打表工具只是出包流程中的其中一步,如果其他cmd文件要调用exportToClIEnt.cmd, cd /d %~dp0这句话的意义就很重要了。切换当前路径

完结语

这套工具是我们项目组同事写的,这一套打表框架我我只贴了一小部分代码,省略了无数代码,本篇文章只梳理核心思想。

 

总结

以上是内存溢出为你收集整理的[游戏开发]Python打表工具处理Excel数据并生成proto和bytes [第二篇] 打表流程全部内容,希望文章能够帮你解决[游戏开发]Python打表工具处理Excel数据并生成proto和bytes [第二篇] 打表流程所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1188353.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-03
下一篇 2022-06-03

发表评论

登录后才能评论

评论列表(0条)

保存