C# Unity游戲開發——Excel中的數據是如何到游戲中的 (二)


本帖是延續的:C# Unity游戲開發——Excel中的數據是如何到游戲中的 (一)

 

上個帖子主要是講了如何讀取Excel,本帖主要是講述讀取的Excel數據是如何序列化成二進制的,考慮到現在在手游中應用很廣泛的序列化技術Google的ProtoBuf,所以本文也是按照ProtoBuf的方式來操作的。ProtoBuf是一個開源庫,簡單來說ProtoBuf就是一個能使序列化的數據變得更小的類庫,當然這里指的更小是相對的。好了ProtBuf的東西就不在多說,以后會專門寫一篇帖子的。本帖其實就相當於上帖中列出的“流程”中的2.將讀取的數據進行序列化並寫入文件。雖然這句話只有16個字,但是他的內容絕對不止16個字這么簡單。

 

一開始計划的是這樣的:1.先把Excel數據讀出來。2.然后根據數據生成相應的類代碼。3.然后再生成代碼的實例並且通過反射(Reflection)把Excel中的數據填進去。4.最后用ProtBuf進行序列化。這聽起來似乎很合理,不過在實現過程中還是遇到了幾個棘手的問題。但是最重要的一個問題是,3.生成代碼的實例並且通過反射(Reflection)把Excel中的數據填進去,中“生成實例”的過程。

 

在運行時生成的代碼知識一串字符而已,想要生成字符串中的類的實例需要用到動態編譯把代碼編譯后生成一個Assembly,然后用Assembly中的CreateInstance方法創建一個object類型的實例,這個實例就是字符串代碼中的類的實例。但是,我們使用了ProtoBuf數據的格式來生成代碼,所以工程中直接把ProtBuf的源碼放進來而不是引入protobuf-net.dll,所以在動態編譯設置編譯參數來動態引入DLL文件的時候經常出錯而導致,編譯生成的Assembly是null。所以考慮到這些本文使用了一種折中的方案來跳過動態編譯這個過程。

因為在Unity項目中我們關注的只是最終生成的二進制文件本身所以對於它是如何生成的對於Unity項目來說是沒有任何影響的,只要保證數據的格式和生成代碼的格式相對應一切就OK。基於此,我們把這個過程分成了兩部分,1.生成代碼。2.生成二進制文件(其實就是把編譯的工作交給了編譯器。。。)OK,話不多說了,帖代碼吧!

 

1.生成代碼

    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..生成二進制文件

    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);

Type.GetType(string)會默認從當前程序及搜索類型,而我的類文件是放到了Unity的Editor文件下,Unity會把這個文件識別為屬於Editor程序集,但是生成的代碼是放到了其他文件夾下Unity會把文件識別為另一個程序集,這個DataManager一定要放到和生成的類文件的相同程序集中。代碼如下

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);
    }
}
View Code

 

將代碼寫入文件的方法

    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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM