Unity3D客户端和Java服务端使用Protobuf

摘要:
你看到了其中的团结吗?Dll中的protobuf网络将由我们使用。a、 Protobuf使用JIT,即在运行时动态编译。Unity发布到iOS时不支持此功能。参见步骤4.4。在Unity中反序列化Protobuf。因为一般游戏客户端请求的数据量相对简单且较小,所以我们的前端请求不是直接的二进制请求。Protobuf仅在前端从后端接收返回时使用。这里只讨论Protobuf的反序列化。

原文:http://blog.csdn.net/kakashi8841/article/details/17334493

前几天有位网友问我关于Unity3D里面使用Protobuf的方法,一时有事拖到现在才写这篇文章,不好意思哈。

本文测试环境:

系统:WINDOWS 7(第3、6步)、OS X 10.9(第4步)

软件:VS 2012(第3、6步)、Eclipse(第5、6步)

硬件:iPad 2(第4步)、Macbook Pro Mid 2012(第4步)

文章目录:

1、关于Protobuf的C#实现

2、为什么有些Protobuf发布到iOS就用不了,甚至有些在PC都用不了?

3、手动处理C#版本的Protobuf

3.1、创建一个C#工程,先手动创建每一个要通过Protobuf序列化或反序列化的数据模型类,然后导出dll

3.2、创建一个用于序列化的C#工程,然后运行生成dll

3.3、将上面两个工程生成的dll拖到unity中

4、在Unity中反序列化Protobuf

5、服务端Java也用Protobuf

6、太烦了?!客户端也要自动处理Protobuf

1、关于Protobuf的C#实现

首先,U3D里面Protobuf使用的是C#的实现,那么目前有几个可选的C#实现:

C#: http://code.google.com/p/protobuf-csharp-port
C#: http://code.google.com/p/protosharp/
C#: https://silentorbit.com/protobuf/
C#/.NET/WCF/VB: http://code.google.com/p/protobuf-net/

我这里选用的是http://code.google.com/p/protobuf-net/(你可以在https://code.google.com/p/protobuf-net/downloads/list这里下载到他的代码和工具),它比较好的一点是,提供了各种平台的支持,解压后在“Full”目录中可以看到各个平台的支持。(现在google被各种封杀,如果你打不开上面的地址,可以下载我上传到CSDN的,里面的csharp文件夹就是各个平台的protobuf需要的dll,点击下载protobuf-net)。

Unity3D客户端和Java服务端使用Protobuf第1张

看到里面的unity了吗,它里面的protobuf-net.dll将是我们准备用到的。

2、为什么有些Protobuf发布到iOS就用不了,甚至有些在PC都用不了?

a、Protobuf使用了JIT,即在运行时动态编译,而这个特性在Unity发布到iOS时候是不支持的。因此,会导致你在PC上可以正常运行,发布到iOS就有问题。

b、Protobuf是基于.net 2.0以上框架写的,而Unity仅支持.net 2.0,或者有些使用2.0中比较多的特性,而你在Unity中发布设置了.net 2.0的子集。后者你只需要在Player setting中修改设置就可以了。

上面两项也可适用于其它第三方类库,如果你自己下载了一个在PC上或C#里面能正常使用的类库,在U3D里面就不能用了,那么请检查是否是上面两条原因导致的。

3、手动处理C#版本的Protobuf

知道了上面问题,我们只要选一个.net2.0的Protobuf,然后它又不是JIT,那就可以正常使用了。

这里用的思路是:

3.1、创建一个C#工程,先手动创建每一个要通过Protobuf序列化或反序列化的数据模型类,然后导出dll

以VS为例,首先,创建一个类库工程:“文件”>"新建">"项目">"类库"(记得选择 .net framework 2.0)

Unity3D客户端和Java服务端使用Protobuf第2张

将unity的protobuf的dll添加到项目引用

Unity3D客户端和Java服务端使用Protobuf第3张

然后假设你有一个类WorkerInfo是需要通过Protobuf进行序列化和反序列化的,那么创建一个WorkerInfo类,内容如下:

  1. usingSystem;
  2. usingSystem.Collections.Generic;
  3. usingSystem.Text;
  4. usingProtoBuf;
  5. namespaceCom.YourCompany.Project.Proto.Module{
  6. [ProtoContract]
  7. publicclassWorkerInfo{
  8. [ProtoMember(1)]
  9. publicintworkerId;
  10. [ProtoMember(2)]
  11. publicintleftClosingTimeSec;
  12. [ProtoMember(3)]
  13. publicintbuildingId;
  14. }
  15. }

按下Shift+F6生成dll,在项目的binDebug目录下就可以找到ProtoModelDLL.dll了

3.2、创建一个用于序列化的C#工程,然后运行生成dll
也是以VS为例,首先创建一个控制台应用程序:“文件”>"新建">"项目">"控制台应用程序"(记得选择 .net framework 2.0)

Unity3D客户端和Java服务端使用Protobuf第6张

将Protobuf和3.1生成的dll添加到引用

Unity3D客户端和Java服务端使用Protobuf第7张

在项目生成的Program.cs中写入:

  1. usingSystem;
  2. usingSystem.Collections.Generic;
  3. usingSystem.Text;
  4. usingProtoBuf.Meta;
  5. usingProtoBuf;
  6. usingProtoBuf.Compiler;
  7. usingCom.YourCompany.Project.Proto.Module;
  8. namespaceProtoModelSerializerCreator
  9. {
  10. classProgram
  11. {
  12. staticvoidMain(string[]args)
  13. {
  14. varmodel=TypeModel.Create();
  15. model.Add(typeof(object),true);
  16. model.Add(typeof(WorkerInfo),true);
  17. model.AllowParseableTypes=true;
  18. model.AutoAddMissingTypes=true;
  19. model.Compile("ProtoModelSerializer","ProtoModelSerializer.dll");
  20. }
  21. }
  22. }

然后ctrl+F5运行,这时候你就可以在binDebug中看到ProtoModelSerializer.dll。

3.3、将上面两个工程生成的dll(ProtoModelDLL.dll和ProtoModelSerializer.dll)以及protobuf-net.dll拖到unity中

怎么用?看第4步

4、在Unity中反序列化Protobuf

由于一般游戏客户端请求的数据量比较简单,也比较少,因此我们前端请求是不直接二进制请求。而前端收到后端返回才采用了Protobuf,因此。这里只讨论Protobuf的反序列化。

代码很简单,下面写个测试代码:

  1. usingUnityEngine;
  2. usingSystem.Collections;
  3. usingProtoBuf.Meta;
  4. usingCom.YourCompany.Project.Proto.Module;
  5. usingSystem.IO;
  6. usingCom.Duoyu001.Proto.Building;
  7. usingCom.Duoyu001.Proto.Worker;
  8. publicclassTestProto:MonoBehaviour
  9. {
  10. //init
  11. voidStart()
  12. {
  13. byte[]dataFromServ=newbyte[]{8,233,7,16,100,24,1};//thesebytesaregeneratedbyserver
  14. RuntimeTypeModelserializer=ProtoModelSerializer.Create();
  15. System.IO.MemoryStreammemStream=newSystem.IO.MemoryStream();
  16. WorkerInfow=newWorkerInfo();
  17. serializer.Deserialize(memStream,w,w.GetType());//asignvaluetoprotomodel
  18. Debug.Log(w.workerId+","+w.buildingId+","+w.leftClosingTimeSec);
  19. }
  20. }

Unity3D客户端和Java服务端使用Protobuf第12张

运行后Unity控制台输出了worker的信息。代码中的dataFromServ字节数组的内容实际应该是通信时候后端返回的。这里测试就不涉及Socket通信的知识了。

5、服务端Java也用Protobuf

看到一个客户端用Protobuf这么麻烦,那后端会怎样呢?其实后端是比较简单的,有Google官方的支持。

下载:https://code.google.com/p/protobuf/downloads/list

下完解压是这样的(2.5.0):

Unity3D客户端和Java服务端使用Protobuf第13张

进入“protobuf-2.5.0java”文件夹,里面是一个maven项目,你直接用maven clean install在target目录就会生成一个protobuf-java-2.5.0.jar的jar包了,没maven的在这里下载吧,我用maven生成的http://download.csdn.net/detail/kakashi8841/6723689。这个要时候导入你的Java项目。就可以了。

然后你写一个proto文件,调用“protobuf-2.5.0src”里面的protoc.exe进行生成,它会帮你生成一个java文件。(详细看https://developers.google.com/protocol-buffers/docs/javatutorial),我这里有提供一个bat,用于调用protoc来生成java文件的,手动输入的话太麻烦了。在windows下把它保存到.bat然后双击运行就可以了。

  1. @echooff
  2. echo**settingruntimevariable
  3. REM_protoSrc是你的proto文件目录的位置
  4. set_protoSrc=F:project_proto_src runkxgame-controllersprotos
  5. REMprotoExe是用于从proto生成java的protoc.exe程序的位置
  6. setprotoExe=C:UsersjohnDesktopprotobuf-2.5.0srcprotoc.exe
  7. REMjava_out_file存放生成的Java文件目录的位置
  8. setjava_out_file=F:project_proto_src runkxgame-controllerssrcmainjava
  9. for/R"%_protoSrc%"%%iin(*)do(
  10. setfilename=%%~nxi
  11. if"%%~xi"==".proto"(
  12. %protoExe%--proto_path=%_protoSrc%--java_out=%java_out_file%%%i
  13. )
  14. )

OK啦,这样你只要把生成的Java复制到或直接生成到你的Java项目源码目录中,然后就可以使用了。比如:
以前面说的WorkerInfo为例

  1. packagecom.duoyu001.xgame;
  2. importjava.util.Arrays;
  3. importcom.duoyu001.xgame.worker.proto.WorkerInfoBuilder.WorkerInfo;
  4. publicclassTestProto{
  5. publicstaticvoidmain(String[]args){
  6. WorkerInfow=WorkerInfo.newBuilder().setBuildingId(1)
  7. .setLeftClosingTimeSec(100).setWorkerId(1001).build();
  8. byte[]byteArray=w.toByteArray();
  9. System.out.println(Arrays.toString(byteArray));
  10. }
  11. }

控制台就会输出:
Unity3D客户端和Java服务端使用Protobuf第18张

细心的同学会发现这里的字节和上面的“8, 233, 7, 16, 100, 24, 1”有点不太一样。第二个数字分别为-23和233.

其实,这个只是byte的有无符号的表示差异而已。

他们的二进制表示都是:11101001

6、太烦了?!客户端也要自动处理Protobuf

我们看到客户端没加一个类,都需要在两个VS项目中增加代码,而后端是直接根据proto文件生成代码的。这样,好像有点不公平,而且这样前后端还是可能会写出不一样的结构。其实用protobuf我个人觉得最大的好处:

a、数据量小

b、通过proto模板生成代码,减少前后端联调

但是现在只是后端减少了工作,前端并没有减少,因此第二个好处不是很明显。

那好吧。我没有就这样满足。因此,我决定,前端也要根据proto来生成cs文件。

因此,我使用Java编写了一个解析Proto文件的工具,并且根据proto生成cs文件,然后用bat调用vs的命令行执行vs项目的构建,最后生成两个dll。

我把上面的命令都整合到一个bat中,因此这个bat的任务是:

执行java程序,把proto文件生成cs文件。

调用vs接口构建两个vs项目,生成两个dll。

通过svn把这两个dll提交到客户端的主干上。

调用上面根据proto生成java的bat片段,生成java代码。

通过svn把生成的java代码提交到服务端主干上。

因此,这样一来,现在只要编写好proto文件,然后双击bat,前后端就都可以通过更新svn来获取最新的Protobuf数据对象。

bat运行

Unity3D客户端和Java服务端使用Protobuf第19张

根据proto生成cs文件并编译执行两个vs项目,然后把生成的dll提交到svn上

Unity3D客户端和Java服务端使用Protobuf第20张

生成java代码并提交svn

这一步相关操作大家有兴趣可以自行试试,有问题欢迎在本博客讨论。

免责声明:文章转载自《Unity3D客户端和Java服务端使用Protobuf》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇PostgreSQL和Oracle的sql差异keepalived日志下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

unity3d开发环境配置

1. 首先先下载软件包:http://pan.baidu.com/s/1imYVv  4.2版本2.下载完后,解压会看到两个文件(运行第二个安装包) 3.准备安装,这里直接上图了。 这里全选,里面包括运行媒体之类的。 这里自己选择安装目录。如果你C盘空间不够,可以选择其他盘 开始安装了,等待几分钟 安装成功,去掉勾选Run暂时不需要启动。   ...

Django Rest_Framework(drf)介绍,以及安装和配置

1、Django Rest_Framework核心思想: 缩减编写api接口的代码 Django REST framework是一个建立在Django基础之上的Web 应用开发框架,本质上就是一个内置在django里面的子应用,可以快速的开发REST API接口应用。 在REST framework中,提供了序列化器对象Serializer的定义,可以帮助...

unity客户端与c++服务器之间的简单通讯_1

// 服务器 # pragma once using namespace std; # include <iostream> # include <string> # include <stdio.h> # include <winsock2.h> # pragma comment(lib,...

Unity里面的自动寻路(二)

        接着我的 上一篇自动寻路文章,这一次我们就来学习一下与自动寻路有关的组件吧。Unity中与自动寻路相关的组件主要有两个:NavMeshAgent (  又称导航网格代理 ),Off Mesh Link( 分离网格链接 )。这两个组件的作用与使用范围是不同的,我们唯一可以确定的是我们必须烘焙地形,产生NavMesh(导航网格)。因为导航网格决...

Unity多语言本地化改进版

简介 之前捣鼓过一个通过csv配置游戏多语言支持的小工具,但是发现使用过程中,通过notepad++去进行转码很不方便,并且直接将配置的csv不加密的放在游戏中心里感觉不是很踏实 于是乎~~ 新的方案 1.在PC/MAC平台上解析多语言配置,也就是editor运行环境中解析csv或者excel 2.通过在Editor运行过程中生成多个语言对象,然后序列化并...

C#序列化和反序列化

序列化和反序列化我们可能经常会听到,其实通俗一点的解释,序列化就是把一个对象保存到一个文件或数据库字段中去,反序列化就是在适当的时候把这个文件再转化成原来的对象使用。序列化和反序列化最主要的作用有:1、在进程下次启动时读取上次保存的对象的信息2、在不同的AppDomain或进程之间传递数据3、在分布式应用系统中传递数据 引用:http://www.cnbl...