游戏资源自动转换成Lua表
关于这个问题,几年前已经做过一个工具,自动导出成一个c++的struct,然后用vector存储这些数据,实践效果并不好。现在用Lua与GO重写了一下,可以导出lua及json数据格式。
一、在制定Excel表时需要遵守以下几个规则:
1.第一行为变量类型,变量类型为三种:数字, 布尔值,字符串(n, b, s)
2.第二行为列名,只能为英文名,且不能有空格
3.列名前缀不能包含xxxxx_、ooooo_
4.特殊列名valid只能用来控制是否导出该行数据,不作它用
规则还是比较简单的,现有表fate.xlsx如下:
数据可以从第2行之后的任意行开始,我这里从第4行开始,中间可以留给策划写注释或者给列名取中文名。
二、导出时需要自定义一个列的table表,table表的左侧key值可以是自己命名的key值,也可以是来自原excel表的列名,右侧value则一定是原excel的列名。示例如下:
local head_struct = { id = 'id', fate = 'fate', servant = { [1] = { id = 'servant1_id', name = 'name1' }, [2] = { id = 'servant2_id', name = 'name2' }, [3] = { id = 'servant3_id', name = 'name3' }, }, player = { [right_name('servant1_id')] = { id = 'servant1_id', name = 'name1' }, [right_name('servant2_id')] = { id = 'servant2_id', name = 'name2' }, [right_name('servant3_id')] = { id = 'servant3_id', name = 'name3' }, }, type = table_name('type'), type2 = table_name('type2'), min = 'min', max = 'max', correct = 'correct' }
注意:
1.head_struct表的左侧有用到excel表的servant2_id字段,需要用right_name函数包裹,程序会取出server2_id字段里的数据作为key值。
2.如果策划想一个字段里包含多个字段,像上表里的type, type2字段一样,则需要策划略懂lua的table表语法。用table_name函数包裹后,会生成table数据。
三、最后导出的数据可以是lua表也可以是json,现贴出lua数据:
g_fate_cfg = { [1] = { ["type2"] = { [1] = { ["id"] = 1, ["cnt"] = 2, }, [2] = { ["id"] = 1, ["cnt"] = 2, }, }, ["type"] = { [1] = "1", [2] = 2, [3] = 3, }, ["id"] = 1, ["correct"] = true, ["min"] = 2000, ["servant"] = { [1] = { ["id"] = 800032, ["name"] = "灞波儿奔", }, [2] = { ["id"] = 800033, ["name"] = "老鼋", }, [3] = { ["id"] = 800034, ["name"] = "", }, }, ["fate"] = "卖主求荣", ["player"] = { [800034] = { ["id"] = 800034, ["name"] = "", }, [800033] = { ["id"] = 800033, ["name"] = "老鼋", }, [800032] = { ["id"] = 800032, ["name"] = "灞波儿奔", }, }, ["max"] = 5000, }, [2] = { ["type2"] = { [1] = { ["id"] = 1, ["cnt"] = 2, }, [2] = { ["id"] = 1, ["cnt"] = 2, }, }, ["type"] = { [1] = "1", [2] = 2, [3] = 3, }, ["id"] = 2, ["correct"] = false, ["min"] = 2000, ["servant"] = { [1] = { ["id"] = 800032, ["name"] = "灞波儿奔", }, [2] = { ["id"] = 800031, ["name"] = "奔波儿灞", }, [3] = { ["id"] = 800028, ["name"] = "蛇后", }, }, ["fate"] = "三贱客", ["player"] = { [800031] = { ["id"] = 800031, ["name"] = "奔波儿灞", }, [800032] = { ["id"] = 800032, ["name"] = "灞波儿奔", }, [800028] = { ["id"] = 800028, ["name"] = "蛇后", }, }, ["max"] = 5000, }, [3] = { ["type2"] = { [1] = { ["id"] = 1, ["cnt"] = 2, }, [2] = { ["id"] = 1, ["cnt"] = 2, }, }, ["type"] = { [1] = "1", [2] = 2, [3] = 3, }, ["id"] = 3, ["correct"] = true, ["min"] = 2000, ["servant"] = { [1] = { ["id"] = 800016, ["name"] = "白骨精", }, [2] = { ["id"] = 800027, ["name"] = "百花羞", }, [3] = { ["id"] = 800028, ["name"] = "", }, }, ["fate"] = "金兰之好", ["player"] = { [800016] = { ["id"] = 800016, ["name"] = "白骨精", }, [800027] = { ["id"] = 800027, ["name"] = "百花羞", }, [800028] = { ["id"] = 800028, ["name"] = "", }, }, ["max"] = 5000, }, [4] = { ["type2"] = { [1] = { ["id"] = 1, ["cnt"] = 2, }, [2] = { ["id"] = 1, ["cnt"] = 2, }, }, ["type"] = { [1] = "1", [2] = 2, [3] = 3, }, ["id"] = 4, ["correct"] = true, ["min"] = 1000, ["servant"] = { [1] = { ["id"] = 800016, ["name"] = "白骨精", }, [2] = { ["id"] = 800063, ["name"] = "孙小空", }, [3] = { ["id"] = 800028, ["name"] = "", }, }, ["fate"] = "失之交臂", ["player"] = { [800016] = { ["id"] = 800016, ["name"] = "白骨精", }, [800063] = { ["id"] = 800063, ["name"] = "孙小空", }, [800028] = { ["id"] = 800028, ["name"] = "", }, }, ["max"] = 3000, }, }
基本上可以满足现实需要。
Excel数据读取部分我采用GO做的,读出来后保存为*_tmp.lua的临时数据。里面是一个2维的table表,与excel表一一对应。GO代码如下:
1 /*------------------------------------------------------------------ 2 // 著作版权:Copyright (C) liuxb 3 // 创建时间:[liuxb|20160704] 4 // 功能描述:字符串转换 5 // 6 // 修改时间: 7 // 修改描述: 8 // 9 //----------------------------------------------------------------*/ 10 11 package main 12 13 import ( 14 "flex/log" 15 "github.com/tealeg/xlsx" 16 "os" 17 "strings" 18 "strconv" 19 ) 20 21 type column struct { 22 name string 23 type_ string 24 } 25 26 type table struct { 27 columns []column 28 rows [][]string 29 } 30 31 var valid string 32 33 func init() { 34 valid = "valid" 35 } 36 37 func getStrings(cells []*xlsx.Cell) []string { 38 str := make([]string, 0) 39 for _, cell := range cells { 40 s, err := cell.String() 41 if err != nil { 42 log.Error(err.Error()) 43 s = "" 44 } 45 str = append(str, s) 46 } 47 return str 48 } 49 50 func getColumn(types []string, names []string) []column { 51 cols := make([]column, 0) 52 if len(types)<len(names) { 53 for i := len(types); i<len(names); i++ { 54 types = append(types, "s") 55 } 56 } 57 for i := 0; i<len(types); i++ { 58 types[i] = strings.ToLower(types[i]) 59 switch types[i] { 60 case "n": 61 case "i": 62 types[i] = "n" 63 case "int": 64 types[i] = "n" 65 case "float": 66 types[i] = "n" 67 case "number": 68 types[i] = "n" 69 case "b": 70 case "bool": 71 types[i] = "b" 72 case "string": 73 types[i] = "s" 74 default: 75 types[i] = "s" 76 } 77 } 78 for i:= 0; i<len(names); i++ { 79 if names[i] == "" { 80 break 81 } 82 col := column{ 83 name : names[i], 84 type_: types[i], 85 } 86 v := strings.ToLower(col.name) 87 if v == valid { 88 col.name = v 89 } 90 cols = append(cols, col) 91 } 92 return cols 93 } 94 95 func getRow(cols []column, vals []string) []string { 96 if len(vals)<len(cols) { 97 for i := len(vals); i<len(cols); i++ { 98 vals = append(vals, "") 99 } 100 } 101 if len(vals)>len(cols) { 102 vals = vals[0:len(cols)] 103 } 104 for i:= 0; i<len(cols); i++ { 105 if cols[i].name == valid { 106 v := strings.ToLower(vals[i]) 107 if v == "false" || v == "0" { 108 return nil 109 } 110 } 111 } 112 return vals 113 } 114 115 func getTable(start int, rows []*xlsx.Row) *table { 116 if len(rows)<4 || len(rows)<start { 117 return nil 118 } 119 t := new(table) 120 t.rows = make([][]string, 0) 121 122 types := getStrings(rows[0].Cells) 123 names := getStrings(rows[1].Cells) 124 cols := getColumn(types, names) 125 t.columns = cols 126 for i := start; i<len(rows); i++ { 127 row := getRow(cols, getStrings(rows[i].Cells)) 128 if row != nil { 129 t.rows = append(t.rows, row) 130 } 131 } 132 return t 133 } 134 135 func toLuaTable(tableName string, t *table) string { 136 lines := "g_tmp_" + tableName + " = {\n" 137 if t!=nil { 138 for id, row := range t.rows { 139 lines = lines + "\t[" + strconv.Itoa(id+1) + "] = {\n" 140 for k, v := range row { 141 if t.columns[k].name != valid { 142 line := "\t\t" + t.columns[k].name + " = " 143 if t.columns[k].type_ == "s" { 144 line = line + "\"" + v + "\",\n" 145 }else if t.columns[k].type_ == "b" { 146 v = strings.ToLower(v) 147 if v == "true" || v == "1" { 148 v = "true" 149 }else { 150 v = "false" 151 } 152 line = line + v + ",\n" 153 }else{ 154 _, err := strconv.ParseFloat(v, 32) 155 if err != nil { 156 v = "nil" 157 } 158 line = line + v + ",\n" 159 } 160 lines = lines + line 161 } 162 } 163 lines = lines + "\t},\n" 164 } 165 } 166 lines = lines + "}" 167 return lines 168 } 169 170 func main() { 171 log.SetFilename("xlsx") 172 excelFileName := os.Args[1] 173 outPath := os.Args[2] 174 start, e := strconv.Atoi(os.Args[3]) 175 if e!=nil { 176 log.Error(e.Error()) 177 return 178 } 179 xlFile, err := xlsx.OpenFile(excelFileName) 180 if err != nil { 181 log.Error(err.Error()) 182 return 183 } 184 for _, sheet := range xlFile.Sheets { 185 fileName := outPath + "\\" + sheet.Name + "_tmp.lua" 186 file, err := os.Create(fileName) 187 if err != nil { 188 log.Error(err.Error()) 189 return 190 } 191 t := getTable(start-1, sheet.Rows) 192 luaStr := toLuaTable(sheet.Name, t) 193 file.WriteString(luaStr) 194 } 195 }
将临时数据转换为自定义的结构导出,我用了两个lua文件,分别为import_base.lua:
1 --[[*------------------------------------------------------------------ 2 // 著作版权:Copyright (C) liuxb 3 // 创建时间:[liuxb|20160704] 4 // 功能描述:将二维数据表转换为自定义表结构 5 // 6 // 修改时间: 7 // 修改描述: 8 // 9 //----------------------------------------------------------------]] 10 11 local table_word = 'ooooo_' 12 local right_word = 'xxxxx_' 13 14 function table_name(name) 15 return table_word..name 16 end 17 18 function right_name(name) 19 return right_word..name 20 end 21 22 local function string_to_table(str) 23 local ret = loadstring("return "..str)() 24 return ret 25 end 26 27 local head_struct_sample = { 28 id = 'id', 29 name = 'name', 30 buff = { 31 [1] = 'buff_id1', 32 [2] = 'buff_id2', 33 }, 34 skill = { 35 [1] = { 36 id = 'skill_id', 37 level = 'skill_level', 38 }, 39 [2] = { 40 id = 'skill_id2', 41 level = 'skill_level2', 42 } 43 }, 44 skill2 = { 45 [right_name('skill_id')] = { 46 id = 'skill_id', 47 level = 'skill_level', 48 }, 49 [right_name('skill_id2')] = { 50 id = 'skill_id2', 51 level = 'skill_level2', 52 } 53 }, 54 reward = table_name('reward'), 55 } 56 57 58 59 -- 简单结构,如:id = id 60 function field_sample(row, name, original_row, original_name) 61 if original_row[original_name] == nil then 62 print('not exist column name:'..original_name..' in xlsx line 62') 63 --return 1/nil 64 else 65 row[name] = original_row[original_name] 66 end 67 end 68 69 -- 字符串转table结构 70 function field_string_table(row, name, original_row, original_name) 71 org_name = string.sub(original_name,7) 72 if org_name == nil or original_row[org_name] == nil then 73 print('not exist column name:'..(org_name or original_name)..' in xlsx line 73') 74 --return 1/nil 75 else 76 row[name] = string_to_table(original_row[org_name]) 77 end 78 end 79 80 local function create_row(head_struct, row, t) 81 if t == nil then t = {} end 82 for k, v in pairs(head_struct) do 83 start, _ = string.find(k, right_word) 84 if start ~= 1 then -- new key 85 if type(v) == "string" then 86 start, _ = string.find(v, table_word) 87 if start == 1 then -- str_table 88 field_string_table(t, k, row, v) 89 else 90 field_sample(t, k, row, v) 91 end 92 elseif type(v) == "table" then 93 t[k] = {} 94 t[k] = create_row(v, row, t[k]) 95 else 96 print('not exist column name:'..v..' in xlsx line 96') 97 return 1/nil 98 end 99 else -- key from xlsx 100 k = string.sub(k,7) 101 if k == nil or row[k] == nil then 102 print('not exist column name:'..(k or 'nil')..' in xlsx line 102') 103 return 1/nil 104 end 105 if type(v) == "string" then 106 start, _ = string.find(v, table_word) 107 if start == 1 then -- str_table 108 field_string_table(t, row[k], row, v) 109 else 110 field_sample(t, row[k], row, v) 111 end 112 elseif type(v) == "table" then 113 t[row[k]] = {} 114 t[row[k]] = create_row(v, row, t[row[k]]) 115 else 116 print('not exist column name:'..v..' in xlsx line 116') 117 return 1/nil 118 end 119 end 120 end 121 return t 122 end 123 124 function make_table(head_struct, original_table, index_name) 125 if index_name == nil then 126 index_name = 'id' 127 end 128 local t = {} 129 for _, row in pairs(original_table) do 130 local r = create_row(head_struct, row) 131 t[r[index_name]] = r 132 end 133 return t 134 end
import_base_ex.lua中主要是将数据写入文本:
1 --[[*------------------------------------------------------------------ 2 // 著作版权:Copyright (C) liuxb 3 // 创建时间:[liuxb|20160704] 4 // 功能描述:将自定义表结构导出文本 5 // 6 // 修改时间: 7 // 修改描述: 8 // 9 //----------------------------------------------------------------]] 10 11 assert(require 'import_base') 12 require('std') 13 14 require("json") 15 --output_server_path = [[../../../Server/data/lua/data/]] 16 output_server_path = [[./data_lua/]] 17 out_json_path = [[./data_json/]] 18 19 20 21 function make_server_lua_file(name, data) 22 local name_adj = 'g_'.. name .. '_cfg' 23 local content = name_adj .. ' = ' .. prettytostring(data) 24 local out_name_adj = 'd_' .. name .. '.lua' 25 local out_full = output_server_path .. out_name_adj 26 local f = io.open(out_full,'w+') 27 print(out_full) 28 f:write(content) 29 f:close() 30 end 31 32 function make_json_file(name, data) 33 local content = json.encode(data) 34 local out_full = out_json_path..name..'.json' 35 local f = io.open(out_full, 'w+') 36 print(out_full) 37 f:write(content) 38 f:close() 39 end
接下来是最后一个fate.lua,每个excel表都需要写一个lua文件,用来存放最开始展示的自定义表结构:head_struct。
1 package.path = package.path .. ";.\\base\\?.lua;.\\tmp\\?.lua" 2 require 'os' 3 assert(require 'import_base') 4 assert(require 'import_base_ex') 5 assert(require 'fate_tmp') 6 7 local head_struct = { 8 id = 'id', 9 fate = 'fate', 10 servant = { 11 [1] = { 12 id = 'servant1_id', 13 name = 'name1' 14 }, 15 [2] = { 16 id = 'servant2_id', 17 name = 'name2' 18 }, 19 [3] = { 20 id = 'servant3_id', 21 name = 'name3' 22 }, 23 }, 24 player = { 25 [right_name('servant1_id')] = { 26 id = 'servant1_id', 27 name = 'name1' 28 }, 29 [right_name('servant2_id')] = { 30 id = 'servant2_id', 31 name = 'name2' 32 }, 33 [right_name('servant3_id')] = { 34 id = 'servant3_id', 35 name = 'name3' 36 }, 37 }, 38 type = table_name('type'), 39 type2 = table_name('type2'), 40 min = 'min', 41 max = 'max', 42 correct = 'correct' 43 } 44 45 function make_fate_lua() 46 local t = make_table(head_struct, g_tmp_fate, 'id') 47 make_server_lua_file('fate', t) 48 end 49 50 function make_fate_json() 51 local t = make_table(head_struct, g_tmp_fate, 'id') 52 make_json_file('fate', t) 53 end 54 55 os.execute("xlsx.exe .\\xlsx\\fate.xlsx .\\tmp\\ 4") 56 57 make_fate_lua() 58 make_fate_json()