本帖是延續的:C# Unity遊戲開發——Excel中的數據是如何到遊戲中的 (一)html
上個帖子主要是講了如何讀取Excel,本帖主要是講述讀取的Excel數據是如何序列化成二進制的,考慮到如今在手遊中應用很普遍的序列化技術Google的ProtoBuf,因此本文也是按照ProtoBuf的方式來操做的。ProtoBuf是一個開源庫,簡單來講ProtoBuf就是一個能使序列化的數據變得更小的類庫,固然這裏指的更小是相對的。好了ProtBuf的東西就不在多說,之後會專門寫一篇帖子的。本帖其實就至關於上帖中列出的「流程」中的2.將讀取的數據進行序列化並寫入文件。雖然這句話只有16個字,可是他的內容絕對不止16個字這麼簡單。ide
一開始計劃的是這樣的:1.先把Excel數據讀出來。2.而後根據數據生成相應的類代碼。3.而後再生成代碼的實例而且經過反射(Reflection)把Excel中的數據填進去。4.最後用ProtBuf進行序列化。這聽起來彷佛很合理,不過在實現過程當中仍是遇到了幾個棘手的問題。可是最重要的一個問題是,3.生成代碼的實例而且經過反射(Reflection)把Excel中的數據填進去,中「生成實例」的過程。測試
在運行時生成的代碼知識一串字符而已,想要生成字符串中的類的實例須要用到動態編譯把代碼編譯後生成一個Assembly,而後用Assembly中的CreateInstance方法建立一個object類型的實例,這個實例就是字符串代碼中的類的實例。可是,咱們使用了ProtoBuf數據的格式來生成代碼,因此工程中直接把ProtBuf的源碼放進來而不是引入protobuf-net.dll,因此在動態編譯設置編譯參數來動態引入DLL文件的時候常常出錯而致使,編譯生成的Assembly是null。因此考慮到這些本文使用了一種折中的方案來跳過動態編譯這個過程。this
由於在Unity項目中咱們關注的只是最終生成的二進制文件自己因此對於它是如何生成的對於Unity項目來講是沒有任何影響的,只要保證數據的格式和生成代碼的格式相對應一切就OK。基於此,咱們把這個過程分紅了兩部分,1.生成代碼。2.生成二進制文件(其實就是把編譯的工做交給了編譯器。。。)OK,話很少說了,帖代碼吧!spa
1.生成代碼.net
public static void GenerateCode() { FileInfo info; FileStream stream; IExcelDataReader excelReader; DataSet result; string[] files = Directory.GetFiles(Application.dataPath + "/EasyUI/ExcelFiles", "*.xlsx", SearchOption.TopDirectoryOnly); string staticDataClassCode = ""; try { int priority1 = 1; string code; foreach (string path in files) { info = new FileInfo(path); stream = info.Open(FileMode.Open, FileAccess.Read); excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream); result = excelReader.AsDataSet(); int rowCount = result.Tables[0].Rows.Count; int colCount = result.Tables[0].Columns.Count; string className = result.Tables[1].Rows[2][0].ToString(); staticDataClassCode += " [ProtoMember(" + priority1++ + ")]\n"; staticDataClassCode += " public List<" + className + "> " + className + "List = new List<" + className + ">();\n"; code = ""; code += "using System.Collections;\n"; code += "using System.Collections.Generic;\n"; code += "using ProtoBuf;\n"; code += "[ProtoContract]\n"; code += "public class " + className + "\n"; code += "{\n"; int priority2 = 1; for (int col = 0; col < colCount; col++) { code += " [ProtoMember(" + priority2++ + ")]\n"; code += " public " + result.Tables[1].Rows[1][col].ToString() + " " + result.Tables[1].Rows[0][col].ToString() + ";\n"; } code += " public " + className + "()\n"; code += " {}\n"; code += "}\n"; WriteClass(Application.dataPath + "/Script/Datas/" + className + ".cs", className, code); excelReader.Close(); stream.Close(); } code = ""; code += "using System.Collections;\n"; code += "using System.Collections.Generic;\n"; code += "using ProtoBuf;\n"; code += "[ProtoContract]\n"; code += "public class StaticData\n"; code += "{\n"; code += staticDataClassCode; code += " public StaticData(){}\n"; code += "}\n"; WriteClass(Application.dataPath + "/Script/Datas/StaticData.cs", "StaticData", code); } catch (IndexOutOfRangeException exp) { Debug.LogError(exp.StackTrace); } AssetDatabase.Refresh(); AssetDatabase.SaveAssets(); }
2..生成二進制文件3d
public static void GenerateBinFile() { FileInfo info; FileStream stream; IExcelDataReader excelReader; DataSet result; string[] files = Directory.GetFiles(Application.dataPath + "/EasyUI/ExcelFiles", "*.xlsx", SearchOption.TopDirectoryOnly); int row = 0, col = 0; string name = "", value = "", type = ""; StaticData staticData = new StaticData(); foreach (string path in files) { info = new FileInfo(path); stream = info.Open(FileMode.Open, FileAccess.Read); excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream); result = excelReader.AsDataSet(); int rowCount = result.Tables[0].Rows.Count; int colCount = result.Tables[0].Columns.Count; string className = result.Tables[1].Rows[2][0].ToString(); FieldInfo field = staticData.GetType().GetField(className + "List");//獲取類中的一個Field object fieldValue = field.GetValue(staticData);//給這個實例中的Field的表明的屬性賦值 IList list = fieldValue as IList; for (row = 1; row < rowCount; row++) { Type tt = DataManager.Instance.GetType(className); object obj = System.Activator.CreateInstance(tt); for (col = 0; col < colCount; col++) { name = result.Tables[1].Rows[0][col].ToString(); value = result.Tables[0].Rows[row][col].ToString(); type = result.Tables[1].Rows[1][col].ToString(); Debug.Log(className); FieldInfo ifo = tt.GetField(name); object cvalue = System.ComponentModel.TypeDescriptor.GetConverter(ifo.FieldType).ConvertFrom(value); ifo.SetValue(obj, cvalue); } if (list != null) list.Add(obj); } excelReader.Close(); stream.Close(); } using (FileStream fstream = File.Create(Application.dataPath + "/StaticDatas.bin")) { Serializer.Serialize(fstream, staticData); } AssetDatabase.Refresh(); AssetDatabase.SaveAssets(); }
說明一下DataManager.Instance.GetType(className);excel
Type.GetType(string)會默認從當前程序及搜索類型,而個人類文件是放到了Unity的Editor文件下,Unity會把這個文件識別爲屬於Editor程序集,可是生成的代碼是放到了其餘文件夾下Unity會把文件識別爲另外一個程序集,這個DataManager必定要放到和生成的類文件的相同程序集中。代碼以下code
using UnityEngine; using System.Collections; using System; public class DataManager { private static DataManager _instance; public static DataManager Instance { get { if (_instance == null) _instance = new DataManager(); return _instance; } } public DataManager() { _instance = this; } public Type GetType(string name) { return Type.GetType(name); } }
將代碼寫入文件的方法htm
private static void WriteClass(string path,string className,string code) { System.IO.File.WriteAllText(path, code, System.Text.UnicodeEncoding.UTF8); }
把動態編譯代碼的方法也貼出來吧,方便之後用到
private static Assembly compileCode(string fullClassName, string code) { //建立編譯器 CSharpCodeProvider provider = new CSharpCodeProvider(); //設置編譯參數 CompilerParameters paras = new CompilerParameters(); //paras.ReferencedAssemblies.Add("System.Collections.dll"); //paras.ReferencedAssemblies.Add("System.Collections.Generic.dll"); paras.ReferencedAssemblies.Add("System.dll"); paras.ReferencedAssemblies.Add("protobuf-net.dll"); //paras.OutputAssembly = "ScriptData.dll"; //編譯成exe仍是dll paras.GenerateExecutable = false; //是否寫入內存,不寫入內存就寫入磁盤 paras.GenerateInMemory = true; CompilerResults result = provider.CompileAssemblyFromSource(paras, code);//編譯代碼 Assembly as1 = result.CompiledAssembly; //得到編譯後的程序集 return as1; }
OK,文件已經生成了,經測試兩個10K的Excel文件打包成一個StaticData.bin文件,1K!!!
不過通常遊戲中的數據表不管是內容仍是數量都遠遠大於這個數量。因此通常生成的bin文件還要進行壓縮,或者用Unity打包,哦了,就先到這把剩下的內容下一帖繼續。
http://m.oschina.net/blog/308267
http://dsqiu.iteye.com/blog/1887702
http://www.cnblogs.com/zfanlong1314/p/4197383.html
http://stackoverflow.com/questions/2522376/how-to-choose-between-protobuf-csharp-port-and-protobuf-net
本文固定鏈接:http://www.cnblogs.com/fly-100/p/4541211.html