unity热更方案 java script binding中使用protobuff(二)
来源:互联网 发布:python 日期加减 月份 编辑:程序博客网 时间:2024/05/16 19:52
在上一篇文章中,分析了夜莺的protobuff的使用方案
复习下结论
1.C#类本身,如例子中的ExampleMessage,成员名字与.proto文件中定义的要一致
2.协议字符串,即protobuff中的通用的.proto中的内容,需要注意,不支持import,所以需要把依赖的内容写到一起
3.协议的唯一标识,函数GetMessageName中返回的字符串
我们知道,protobuff-net已经生成了用于序列化的C#类了,我尝试过把生成的类直接转为JS,出现若干问题,但是通过把相关的类导出到JS的方式能够解决转换失败的问题,但是还有运行时的问题,最要命的几点是
[global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"msgVector3")] public partial class msgVector3 : global::ProtoBuf.IExtensible { public msgVector3() {} private long _x = default(long); [global::ProtoBuf.ProtoMember(1, IsRequired = false, Name=@"x", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)] [global::System.ComponentModel.DefaultValue(default(long))] public long x { get { return _x; } set { _x = value; } } private long _y = default(long); [global::ProtoBuf.ProtoMember(2, IsRequired = false, Name=@"y", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)] [global::System.ComponentModel.DefaultValue(default(long))] public long y { get { return _y; } set { _y = value; } } private long _z = default(long); [global::ProtoBuf.ProtoMember(3, IsRequired = false, Name=@"z", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)] [global::System.ComponentModel.DefaultValue(default(long))] public long z { get { return _z; } set { _z = value; } } private global::ProtoBuf.IExtension extensionObject; global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing) { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); } }这是用protobuff-net生成的一个最基础的向量类,有三个分量
//基础3维向量message msgVector3{//xoptional int64 x = 1;//yoptional int64 y = 2;//zoptional int64 z = 3;}
我们看到,.proto文件中定义的成员为xyz,但是在生成的类中,变成了_x _y _z,而在JS运行中,序列化使用的是field功能,名字认的就是成员名字
而不是C#中的property的名字,直接导致序列化失败,需要写个后处理工具,把对应名字改下,生成.cs后再进行处理
还一个问题是
private global::ProtoBuf.IExtension extensionObject; global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing) { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); }这里有个成员 extensionObject,是用来实现protobuff的继承功能,这么强大(鸡肋)的功能我还真没有用过,但是由于 extensionObject也是一个
成员,而且在没有父类的情况下,为null,直接导致序列化失败,悲剧,不过也是有解决办法的,经过分析,假设我们不使用继承功能的前提下,
extensionObject一定是为null的,而且C#运行情况下,需要的是GetExtensionObject这个函数,而不是extensionObject这个变量,所以我们尝试
把extensionObject这个变量删除,然后把函数GetExtensionObject的实现直接改为 return null,OK,妥了,可以序列化了
幸运的是,我做了实验,如果把整个.proto文件作为protoString传入给序列化函数,是可以正确的序列化的
现在,C#类和protoString都已经有了,就差一个protoName了,做法就是,在生成的C#类里加个函数,直接返回protoName,所以改变后的文件长
这样
[global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"Vector3Msg")] public partial class Vector3Msg : global::ProtoBuf.IExtensible { public Vector3Msg() {} public int x = default(int); [global::ProtoBuf.ProtoMember(1, IsRequired = false, Name=@"_x", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)] [global::System.ComponentModel.DefaultValue(default(int))] private int _x { get { return x; } set { x = value; } } public int y = default(int); [global::ProtoBuf.ProtoMember(2, IsRequired = false, Name=@"_y", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)] [global::System.ComponentModel.DefaultValue(default(int))] private int _y { get { return y; } set { y = value; } } public int z = default(int); [global::ProtoBuf.ProtoMember(3, IsRequired = false, Name=@"_z", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)] [global::System.ComponentModel.DefaultValue(default(int))] private int _z { get { return z; } set { z = value; } } global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing) { return null; } public static string GetProtoName() { return "Vector3Msg"; } }
仔细看发现msgVector3变成了Vector3msg,不要在意这些细节,因为我是从不同的工程中获得的资料,然后封装一个类,可以这么写
public class GameSocketInterface{ public static void sendMessage<T>(short cmd, T param, string fileName = null, string protuBufname = null) { if (VersionControl.useJS) { Network.MessageWrap msgWrap = new Network.MessageWrap(); msgWrap.protuBufname = "protocol.msg." + protuBufname; msgWrap.fileName = fileName; GameSocket.Instance.sendStringMessage(cmd, msgWrap.Encode(param)); } else { GameSocket.Instance.sendMessage(cmd, param); } } public static T deserialize<T>(NetMsgParam data, string fileName = null, string protuBufname = null) { if (VersionControl.useJS) { Network.MessageWrap msgWrap = new Network.MessageWrap(); msgWrap.protuBufname = "protocol.msg." + protuBufname; msgWrap.fileName = fileName; return (T) msgWrap.Decode(data.Get64String()); } else { return GameSocket.Instance.deserialize<T>(data.GetBytes()); } }}关于 MessageWrap代码
public class MessageWrap : MessageParent { public string fileName; public string protuBufname; public override string GetProToString() { TextAsset proto = (TextAsset)Resources.Load("@Protos/" + fileName); return proto.ToString(); } public override string GetMessageName() { return protuBufname; } }
关于NetMsgParam的代码
public struct NetMsgParam { private byte[] m_data; public void SetData(byte[] data) { m_data = data; } public string Get64String() { return Convert.ToBase64String(m_data); } public byte[] GetBytes() { return m_data; } }为什么要封装下呢,因为byte[]是从C#传入,然后转到JS的,但是sharpkit不支持直接传入byte[]的转换
序列化使用示例
TeleporterMsg msg = new TeleporterMsg();msg.teleporterTid = teleportId;GameSocketInterface.sendMessage((short)PacketProtocolType.CG_SCENE_SWITCH, msg, "scene", TeleporterMsg.GetProtoName());反序列化示例
private void OnGetAOIInfoMsg(NetMsgParam param) { //根据信息生成角色 SceneAOIInfoMsg msg = GameSocketInterface.deserialize<SceneAOIInfoMsg>(param, "scene", SceneAOIInfoMsg.GetProtoName()); List<SceneObjInfoMsg> objList = msg.aoiObjects; for (int i = 0; i < objList.Count; i++) { SceneObjInfoMsg info = objList[i]; if (info.facadeType == AOIObjFacadeType.PLAYER) { CreateOtherHero(info); } else if (info.facadeType == AOIObjFacadeType.MONSTER) { CreateMoster(info); } } }
我们可以高兴的看到,在使用者层面,已经跟直接用protobuff-net版本写C#代码十分接近了,只是目前还需要传入proto文件的名字,从而获得
proto文件内的字符串获得protoString,而通过设置开关,就可以实现传说级的终极目标:C#写代码,调试,JS发布,热更妥妥的
已经出现太长不看的风险了,再啰嗦几句,其实如果你够勤奋并且有耐心,到这里已经可以实现在JSB使用protobuff了,实际还不够,因为改协议生
成的C#文件太痛苦了,好在我们已经实现了终极解决方案,直接改了protobuff-net的代码生成规则,直接生成符合JS使用的代码,而且兼容C#模式
运行,后续会给大家分享出源代码和原理分析,今天就到这里了~
- unity热更方案 java script binding中使用protobuff(二)
- unity热更方案 java script binding中使用protobuff(一)
- unity热更方案 java script binding中使用protobuff(三)
- unity热更方案 java script binding中使用protobuff(四)
- unity热更方案 java script binding中使用protobuff(收尾)
- Erlang中使用protobuff
- unity-热更1
- Unity热更新方案
- Unity热更新方案
- Unity中使用Script编程小知识(C#语言)
- unity3d 热更dll使用方法(二)
- Unity C#脚本热更
- java热更
- Unity热更新方案(uLua vs sLua)
- Unity热更新方案(uLua vs sLua)
- unity+slua热更流程演示
- Unity 5.3 Assetbundle热更资源
- 【unity系统模块开发】热更
- SVN 常见问题解决
- UVALive 7337 Counting Weekend Days【水题】
- 1508-张晨曦总结《2016年-10月-25日》【连续4天总结】
- Hibernate缓存
- swift 可选型的实际使用
- unity热更方案 java script binding中使用protobuff(二)
- 利用软引用和引用队列构建软引用缓存
- PHP服务器配置之加载硬盘
- Android TV开发总结(五)TV上屏幕适配总结
- Android艺术开发探索——第二章:IPC机制(下)
- Hbuilder更新后启动后报错替代的解决
- python 学习笔记一
- 梯度下降法
- 数据类型与类型识别总结