一个游戏通常需要10多个Excel表格或者更多来配置,一般会通过导出csv格式读取配置。
本文提供导出Excel直接生成c#文件,对应数据直接生成结构体和数组,方便开发排错和简化重复写每个表格的读取配置方法。
上图左图为Excel表格数据,右图 为导出生成的cs文件。
官方文档:https://msdn.microsoft.com/en-us/library/jj620922.aspx
项目环境配置(VS2015配置):
1、根据博客The Visual Studio Blog VS2015需要下载一个Update,其他的VS版本没试过。
2、Microsoft Visual Studio Tools for Applications 2015 SDK
3、Visual Studio 2010 Tools for Office Runtime
依次安装上述SDK,重启VS后新建项目看到可以创建Excel的外接程序了。如下图:
为了在加载项显示对应的按钮图标,需要在项目工程上右键->添加->选择功能区窗口,如下图:
在窗口界面中通过可视化界面可以直接编辑对应的点击按钮功能等。
为了方便导出数据配置,在项目中添加一个App.config:
配置如下:
<?xml version="1.0" encoding="utf-8"?> <configuration> <appSettings> <add key="template" value="// // #SHEET_NAME# 表格名 // // #PARAM_DEF# 定义结构体参数 // #DATA_DEF# 初始化表格数据 // #PAIR_DEF# 定义每个参数数据 需要在#DATA_DEF#定义内 using System; public struct ES_#SHEET_NAME# { #PARAM_DEF# public {0} {1}; #PARAM_END# }; public partial class ESSheet { private static ES_#SHEET_NAME#[] _#SHEET_NAME# = null; public static ES_#SHEET_NAME#[] F#SHEET_NAME# { get { if (_#SHEET_NAME#==null) { _#SHEET_NAME# = new ES_#SHEET_NAME#[] { #DATA_DEF# new ES_#SHEET_NAME# { #PAIR_DEF#{0}={1}#PAIR_END# }, #DATA_END# }; } return _#SHEET_NAME#; } } };" /> </appSettings> </configuration>
正则解析数据代码如下:
// export c# Excel.Worksheet worksheet = (Excel.Worksheet)Globals.ThisAddIn.Application.ActiveSheet; string s = System.Configuration.ConfigurationManager.AppSettings.Get("template"); //去注释 Regex regex = new Regex("//[^\r\n]*[\r\n]+"); var match = regex.Match(s); while (match.Success) { s = s.Replace(match.Value, ""); match = regex.Match(s); } // 替换表格名字 string clsName = worksheet.Name; regex = new Regex(@"#SHEET_NAME#"); match = regex.Match(s); while (match.Success) { s = s.Replace(match.Value, clsName); match = regex.Match(s); } // 参数定义 regex = new Regex("#PARAM_DEF#.*#PARAM_END#", RegexOptions.Singleline); match = regex.Match(s); if (match.Success) { string formatStr = match.Value.Replace("#PARAM_DEF#", ""); formatStr = formatStr.Replace("#PARAM_END#", "").Trim(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < columnCount; i++) { sb.AppendFormat(formatStr, param_types[i], param_names[i]); if (i != columnCount - 1) { sb.AppendLine(); } } s = s.Replace(match.Value, sb.ToString()); } else { MessageBox.Show("#PARAM_DEF# 匹配失败!"); return; } // 数据定义 regex = new Regex("#DATA_DEF#.*#DATA_END#", RegexOptions.Singleline); match = regex.Match(s); if (match.Success) { string matchAllStr = match.Value; string formatStr = matchAllStr.Replace("#DATA_DEF#", ""); formatStr = formatStr.Replace("#DATA_END#", "").Trim(); regex = new Regex("#PAIR_DEF#.*#PAIR_END#", RegexOptions.Singleline); match = regex.Match(formatStr); if (match.Success) { string pairStr = match.Value; string formatStr2 = pairStr.Replace("#PAIR_DEF#", ""); formatStr2 = formatStr2.Replace("#PAIR_END#", "").Trim(); regex = new Regex(@"string"); StringBuilder sb = new StringBuilder(); StringBuilder sb2 = new StringBuilder(); for (int i = 3; i <= rowCount; i++) { sb2.Clear(); for (int j = 1; j <= columnCount; j++) { string stype = param_types[j - 1]; string sname = param_names[j - 1]; bool strflg = regex.Match(stype).Success; var value = (worksheet.Cells[i, j] as Excel.Range).Value; bool nullflg = value == null; if (!nullflg) { string vstr = value.ToString(); vstr = vstr.Trim(); if (string.IsNullOrEmpty(vstr)) { nullflg = true; } else { if (strflg) { vstr = string.Format(""{0}"", vstr); } sb2.AppendFormat(formatStr2, sname, vstr); } } if (nullflg) { // null cell if (strflg) { // string type? sb2.AppendFormat(formatStr2, sname, """"); } else { sb2.AppendFormat(formatStr2, sname, 0); } } if (j != columnCount) { sb2.Append(','); } } sb.Append(formatStr.Replace(pairStr, sb2.ToString())); if (i != rowCount) { sb.AppendLine(); } } s = s.Replace(matchAllStr, sb.ToString()); } else { MessageBox.Show("#PAIR_DEF# 匹配失败!"); return; } } else { MessageBox.Show("#DATA_DEF# 匹配失败!"); return; }
部分说明
1、几个导出定义:第一行 参数名 , 第二行 参数类型 ,第三行起为 数据,表格名为 结构体名字 。
2、Globals.ThisAddIn.Application.ActiveSheet 为当前选中的Excel表格。
3、获取 i 行 j 列数据方法如下:
var value = (worksheet.Cells[i, j] as Excel.Range).Value;
4、这里导出cs文件,如果需要导出cpp文件可以自己修改上面的模板配置即可实现。
由于插件没有存储数据的地方,因此这里将导出路径直接存在了注册表上。
方法如下:
#region RegistStoreData public static readonly string registryPath = @"SoftwareYourCompanyNameYourAddInName"; public static void StoreInRegistry(string keyName, string value) { RegistryKey rootKey = Registry.CurrentUser; using (RegistryKey rk = rootKey.CreateSubKey(registryPath)) { rk.SetValue(keyName, value, RegistryValueKind.String); } } public static string ReadFromRegistry(string keyName, string defaultValue = "") { RegistryKey rootKey = Registry.CurrentUser; using (RegistryKey rk = rootKey.OpenSubKey(registryPath, false)) { if (rk == null) { return defaultValue; } var res = rk.GetValue(keyName, defaultValue); if (res == null) { return defaultValue; } return res.ToString(); } } #endregion
完整代码地址:https://github.com/mydishes/ESExport
可执行插件地址:https://github.com/mydishes/ESExport/tree/master/public