配置的抽取,打包,加載和優化

配置抽取:
前後端的配置時不一樣的,前端配置數據需要從ryzom的配置數據中抽取出來。
簡單介紹Ryzom的配置方式:
生成單個配置的類型的過程和抽象一個類然後對其進行實例化的過程是一致的。
首先是確定基礎Type,這些Type目前支持的string,int,char等基礎類型。
然後就是類的抽象DFN:
一個DFN就是一個類,這個類的屬性可能是Type對應的基礎類型,也可能是由另外的DFN進行類組合出來的類型。
然後就是最後的編輯,爲DFN的每個字段進行填值,這樣就完成了對象的初始化,形成了一個form。
————————————————————————————————————
爲了讓前端更輕鬆的加載資源,覺得將DFN真正的實例化爲對象,解決方案是使用Google Proto Buff的C#版本ProtoGen(http://code.google.com/p/protobuf-csharp-port/wiki/ProtoGen)生成對應的C#類代碼。
——————————————————————————————————————
抽取:
Ryzom的配置文件sheet爲非標準的XML文件。
大概格式爲:
<STRUCT Name = "XX>
    <ATOM Name = "YY" Value = "TT"/>
    <ATOM Name = "ZZ" Value = "GG"/>
    ...........
</STRUCT>
爲了轉化爲C#對象,這種格式的XML是不行的,需要轉化爲標準XML,使用C#原生的運行時序列化XMLSerializer進行序列化到對象。
爲此需要將上面的sheet文件轉化爲同名的xml文件
<XX>
    <YY>TT</YY>
    <ZZ>GG</ZZ>
    ..........
</XX>
寫這個工具是我在藍港入職乾的第一件事。
之前說過每個DFN都是一個類,而下面要進行的XMl序列化和ProtoBuffer對象序列到文件都需要特定的類型,而爲每個DFN改寫成C#Class是有很大的工作量,爲此則需要藉助上面所說的ProtoGen工具來自動生成C#代碼,而爲了生成C#代碼則需要proto文件,然後寫了一個工具來實現從DFN到proto的轉換。將轉化出來的proto在由ProtoGen轉換爲對應的C#代碼
對於這樣的XML文件,C#提供了原生的運行時序列化工具,將一段XML格式字符串用特定的類型序列化成系統object。:
使用到的命名空間:
using System.IO;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Serialization;
/// <summary>
        /// 將xml格式字符串轉化爲Type類型的obj返回
        /// </summary>
        /// <param name="xmlString">xml格式字符串</param>
        /// <param name="type">對象類型</param>
        /// <returns>Type類型的系統obj</returns>
        public static object ReadFromXmlString(string xmlString, Type type)
        {
            // 以type作爲參數new一個XmlSerializer
            XmlSerializer x = new XmlSerializer(type);

            MemoryStream ms = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(xmlString);
            XPathDocument XPDoc = new XPathDocument(ms);
            XPathNavigator XPNav = XPDoc.CreateNavigator();
            XmlReader xmlReader = XPNav.ReadSubtree();

            // 從xml中反序列化出對象實體返回
            return x.Deserialize(xmlReader);
        }
對於這個返回出來的對象則再調用Protobuffer的函數序列化到二進制文件中去:
ProtoBuf.Serializer.SerizeWithLengthPrefix<T(類型)>(FileStream(輸出文件流) ,obj as T(對象),)
{
PS。這裏我曾經嘗試使用C#原生的BinaryFormater進行對象序列化,但是出現了下面的幾個問題:
1。由於原生的序列化中完整保存着對象的所有信息,包括類型以及和其綁定的程序信息,所以序列化出來的對象大小是ProtoBuffer的好幾倍。體積過大對於頁遊和手遊是無法容忍的
2.小容量的對象序列化效率高,但是當數量達到一定程度以後,效率低下。
3.原生的序列化工具兼容性非常差,嚴重依賴C#的高級特性。開始遇到的問題是A程序用指定Type序列化出去的對象在B程序中無法反序列化回來,和A程序綁定了。爲了解綁則需要用到BindToType函數,具體做法寫一個類繼承自SerializationBinder,然後重載其BindToType函數,達到解綁的作用。本來到這問題也解決了,但是後來又發現了一個更爲嚴重的問題:Mono環境不支持BindToType的解綁函數,也就是說原生的C#(VS)寫出來的序列化不支持在Mono中反序列化!
4.標準的C#BinaryFormater支持Dictionary以及SortedDictionary,而Mono不支持。
}
——————————————————————————————————————
打包:
1.Unity不支持繼承自ScriptableObject的嵌套使用,同時不支持List<byte[]> 的原生序列化
2.注意讀入文件的編碼,在加載時需要區分UTF8或者ANSI
爲了解決問題1的折中方式:
FileHolder
{
    List<string> alias; // bin的文件別名,必須和bin一一對應
    List<string> path;// bin文件的路徑
    byte[] bin0;
    byte[] bin1;
    .........
}
打包是指定文件路徑和別名以及對應的bin編號,從文件中加載二進制byte流存入到對應編號的bin中,同時設置alias別名。
取出byte流的前三位判斷是否爲utf8的bom標示:
byte[] b = br.ReadByte(3);
if(b[0] == 239 && b[1] == 187 && b[2] == 191)
如果滿足utf8的條件,則去掉bom標示:fs.Read(bin,3(從索引3開始讀),fs.length-3);
否則釋放fs從新打開文件,從0讀取到文件結束。
——————————————
對於已經生成的FileHolder調用UnityEditor下的
AssetDatabase.CreateAsset(filehoder,filepath);函數生成Asset,然後調用AssetDatabase.SaveAssets();保存Asset。到這僅僅是爲了將文本文件或者其他的數據文件轉化爲Unity的Asset,接下來要進行將Asset壓縮爲AssetBundle:
首先由之前生成的Asset來生成UnityEngine.Object
UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath (filepath,typeof(FileHolder)
將需要打包的Asset轉化爲obj放到一個List中,調用BuildPipeline.BuildAssetBundle函數進行最後壓縮。
對於BuildAssetBundle函數的最後一個參數則需要用宏隔開對於不同的平臺下BuildTarget是不一樣的如WebPlayer,Android等。
————————————————————————————-————
加載:
對於資源的獲取沒什麼好說的,客戶端的可以直接取路徑下的資源,對於WebPlayer版本下的則需要用WWW類來分批次下載,對於具體的下載流程不是很瞭解。
配置文件下載完成後則調用相應的回調進行處理:主要操作就是從二進制流中反序列化到對象中,然後將對象保存在SortedDictionary中。
Sheet的加載流程過慢的解決方案:
1.多線程加載,將Sheet分類,多線程加載。對於同時操作的情況下需要加鎖。這裏還遇到另外一個問題,對於C#的單件Singleton在進行多線程操作的時候,務必要在起線程之前進行初始化,否則可能出現同時對多個單間初始化並操作的問題。(手機版尤其注意)
2.延遲加載,將ProtoBuffer的對象作爲一個屬性存儲在sheet的基類中,同時增加一個字段isLoaded來判斷是否已經加載,如果沒有加載則現加載,否則則直接調用。
3.過濾服務器需要而客戶端不需要的字段。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章