配置的抽取,打包,加载和优化

配置抽取:
前后端的配置时不一样的,前端配置数据需要从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.过滤服务器需要而客户端不需要的字段。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章