Unity教程之-Unity3d热更新动态Protobuff协议

 

首先, Proto协议文件是 服务器和客户端所共享的。 服务器根据它生成对应的语言。   客户端根据它生成对应的语言(Unity中就是C#了)

所以可以想象的是:团队的项目中包括了:服务器project、客户端project、ptotoBuff协议project(这些协议由服务器定义就行了, 然后)

ProtoBuf对于很多人而言并不陌生,应该也在很多项目中得以应用了。 那么现在我们就来看看,在Unity中使用的具体步骤。先看看批处理脚本


@echo off

:: 注: 这是放在Unity的Assets 下的文件
:: cd..是进入上一层目录 cd\是进入根目录

set unityDllPath=%cd%\Assets\Plugins\
cd PXProtocolProject
echo 进入路径: %cd%
set protoSourcePath=%cd%\ProtoFile\
set protoImportsPath=%cd%

:: 进入这个目录
cd CSharpTool
echo 进入路径: %cd%
set protoBinPath=%cd%\ProtobinFile\
set csharpSourceOutputPath=%cd%\CSharpCode\
set csharpProjectPath=%cd%\ProtoDllGen\ProtoProject\ProtoFileToCSharpFile\
:: for %%i in (*.*) do echo %%i

echo generateing csharp codes...
:: 生成C#代码,    for循环对每个文件进行操作
for %%i in (%protoSourcePath%*.*) do (
%cd%\protoc.exe --descriptor_set_out=%protoBinPath%%%~ni.protobin --include_imports %%i --proto_path=%protoImportsPath%
%cd%\protogen.exe %protoBinPath%%%~ni.protobin -output_directory=%csharpSourceOutputPath%
)

:: 将生成的C#文件 拷贝到 Dll工程内
echo copying csharp code file to project..
copy %csharpSourceOutputPath%*.cs %csharpProjectPath%

echo compile the c# dLL project..

set msBuildDir=%WINDIR%\Microsoft.NET\Framework\v2.0.50727
call %msBuildDir%\msbuild.exe  %cd%\ProtoDllGen\ProtoProject\ProtoDllGen.csproj /t:Rebuild /p:Configuration=Release /p:VisualStudioVersion=14.0
:: 将编译的生成文件拷贝到 Unity项目中, 让Unity执行编译
echo copying the dLL to unity project..
copy %cd%\ProtoDllGen\ProtoProject\bin\Release\*.dll %unityDllPath%
pause

:: 要求,这个直接放到unity项目中根目录下。 而这个ptoto项目要相对于Unity项目是平行的。即如下:XXXXX\px\.git  和  XXXXX\px\PXProDoc\.git        // unity项目要 忽略PXProDoc 这个文件夹
:: 然后拷贝 就方便了

看看运行结果:

在 Unity 中双击如下文件:

结果在 Untiy的 文件夹 下:

下面我们来看下步骤:

步骤1 :

Google 官方的Protobuf是不支持C#语言的,好在有很多开发者为ProtoBuf开发了支持C# (.net)的插件, 打开网址 http://code.google.com/p/protobuf-net/, 这是。net版本的主页。

步骤2:

下载 protobuf-net_r668.zip, 将其解压, 然后将路径Full\unity内的所有文件复制到Unity工程目录内,这里建议复制到 Plugins目录内。

步骤3:

使用任意文件编辑器创建第一个协议文件,本地将文件命名为 chatapp.proto,然后定义协议如下:

package ChatAPP;

message User {

required string name = 1;

required string chat = 2;

optional string email = 3;

}

message Chat {

repeated User user = 1;

}

这里我们可以将package看作名称域 , message看作是class, User有3个成员,用户名、聊天内容和 E-mail。

标识符required表示这是个必须的数据, optional表示这是可选的。

在 Chat中,只有一个成员是User,它的前面有一个标识符repeated,表示Chat可以拥有多个 User。

步骤4 :

在 protobuf-net_r668压缩包内有一个 ProtoGen的文件夹, 将chatapp.proto复制到这个文件夹下。

步骤5:

在Windows开始菜单输入 CMD,打开控制台窗口,将路径设置到 ProtoGen,然后输入下面的脚本,转出文件chatapp.cs ,将这个文件复制到客户端和服务器端。

protogen -i:chatapp.proto-o:chatapp.cs

注意,因为每次修改 .proto文件都要重新生成新的.cs 文件,为了方便, 可以将生成 .cs文件的脚本写在一个.cmd文件夹中,这样每次操作只需要选择这个文件即可,如图所示:

步骤6:

在C# 中序列化User的方法如下:

ChatAPP.User user = new ChatAPP.User();

user.name = “用户甲”;

user.chat = “hello,world”;

using (System.IO.MemoryStream stream =new System.IO.MemoryStream())

{

ProtoBuf.Serializer.Serialize<ChatApp.User>(stream, user);

Byte[] bs = Stream.ToArray();

}

步骤7:

从一个byte 数组中解析User的方法如下:

// bs 是一个byte数组, 他应当是从网络或者文件中得到的

using (System.IO.MemoryStreamstream = new System.IO.MemoryStream(bs))

{

ProtoBuf.Meta.RuntimeTypeModelmodel = ChatSerializer.Create();

user2 =(ChatAPP.User)model.Deserialize(stream, null, typeof(ChatAPP.User));

}

步骤8:

repeated类型的数据比较特殊,它在C#中实际是一个数组:

ChatAPP.Chat chat;

chat.user.Add(user);

虽然使用 Protobuf很简单,但当将它使用到 IOS平台会遇到麻烦, .Net版本的ProtoBuf 用到了C#的反射功能,这个功能当前在苹果 ios系统中是不能使用的, 好在.Net 版本的ProtoBuf提供了一个办法解决这个问题。这里以前面创建的chatapp.ptoto这个文件为例,操作步骤如下:

步骤1:

在Visual Studio 中创建要给C# de Class Library 工程,将前面得到的 chatapp.cs 文件添加到这个工程。

步骤2:

在工程中选择 Add Reference 将 Protobuf 提供的 protobuf-net.dll 添加到当前工程,如图所示:

步骤3:

编译当前工程,在输出目录生成一个叫 ProtoLib.dll 的文件, 这个文件的名称并不重要,如图所示:

步骤4:

添加一个新的C# 控制台工程,将protobuf-net.dll 和上一步生成的 ProtoLib.dll 添加到这个工程中, 添加代码如下:

using System;

usingSystem.Collections.Generic;

namespace ProtoBuilder

{

class Program

{

static void Main(string[] args)

{

ProtoBuf.Meta.RuntimeTypeModelmodel =

ProtoBuf.Meta.RuntimeTypeModel.Create();

model.Add(typeof(ChatAPP.Chat),true);

model.Add(typeof(ChatAPP.User),true);

model.Compile(“ChatSerializer”,”ChatSerializer.dll”);

}

}

}

这里的代码首先创建了一个 RuntimeTypeModel 对象, 然后将.proto文件中定义的类都添加到该对象中,最后的参数 ChatSerializer是类的名称域, ChatSerializer.dll 是生成的文件名。

步骤5:

编译当前工程,在输出目录找到ProtoBuilder.exe 文件, 双击运行它,生成ChatSerializer.dll 文件, 如图7-8所示, 将 ChatSerializer.dll 和 ProtoLib.dll 这两个文件都复制到Unity工程中。注意, 在Unity工程中已经不需要 chatapp.cs这个文件, dll文件中已经包括了它。

从最初生成的Protobuf的.cs文件,到反复编译的dll 文件, 在将他们复制到Unity工程中是件非常麻烦的事, 在实际的项目中,最好编写一些批处理脚本来完成这些事情。

步骤6:

在Unity的C# 脚本中序列化 User ,注意与之前代码的不同。

ChatAPP.User user = new ChatAPP.User();

user.name = “用户甲”;

user.chat = “hello,world”;

byte[] bs;

using (System.IO.MemoryStream stream =new System.IO.MemoryStream())

{

ProtoBuf.Meta.RuntimeTypeModelmodel = ChatSerializer.Create();

model.Serialize(stream, user);

bs = stream.ToArray();

}

步骤7:

反序列化的代码如下:

ChatAPP.User user2;

using (System.IO.MemoryStream stream =new System.IO.MemoryStream(bs))

{

ProtoBuf.Meta.RuntimeTypeModelmodel = ChatSerializer.Create();

user2 =(ChatAPP.User)model.Deserialize(stream, null, typeof(ChatAPP.User));

}

每次序列化、反序列化写这么多代码很麻烦,我们可以将他们写为泛型的函数,代码如下:

// 序列化

public static byte[]ProtoRuntimeSerialize<T>(T t)

{

byte[] bs;

using (System.IO.MemoryStream stream =new System.IO.MemoryStream())

{

ProtoBuf.Meta.RuntimeTypeModelmodel = ChatSerializer.Create();

model.Serialize(stream, t);

bs = stream.ToArray();

}

return bs;

}

// 反序列化

public static TProtoRuntimeDeserialize<T>(byte[] bs)

{

T t = default(T);

using (System.IO.MemoryStream stream =new System.IO.MemoryStream(bs))

{

ProtoBuf.Meta.RuntimeTypeModelmodel = ChatSerializer.Create();

t = (T)model.Deserialize(stream,null, typeof(T));

}

return t;

}

现在,只需要简单调用这些函数就可以完成所有的序列化和反序列化工作了、。

ChatAPP.User user = new ChatAPP.User();

user.name = “用户甲”;

user.chat = “hello,world”;

// 序列化

byte[] bs =ProtoRuntimeSerialize<ChatAPP.User>(user);

ChatAPP.User user2;

// 反序列化

user2 =ProtoRuntimeDeserialize<ChatAPP.User>(bs);

Debug.Log(user2.name);

Protobuf除了可以应用到网络中,也可以应用到文件存储中,他的功能强大,是网络开发的利器,值得使用,在unity中双击启动(是Unity的项目路径了) 与 在文件夹中的双击启动(正常的) 是不一样的。 好了,本篇unity3d教程到此结束,下篇我们再会!