Unity网络开发--第三方协议工具Protobuf
什么是 Protobuf
Protobuf 全称是 protocol-buffers(协议缓冲区)
是谷歌提供给开发者的一个开源的协议生成工具
它的主要工作原理和我们之前做的自定义协议工具类似
只不过它更加的完善,可以基于协议配置文件生成
C++、Java、C#、Objective-C、PHP、Python、Ruby、Go 等等语言的代码文件
它是商业游戏开发中常常会选择的协议生成工具
有很多游戏公司选择它作为协议工具来进行网络游戏开发
因为它通用性强,稳定性高,可以节约出开发自定义协议工具的时间
protocol-buffers 官网//https://developers.google.com/protocol-buffers
下载 Protobuf 相关内容 — 准备 DLL 文件
-
在官网中前往下载地址//protocol-buffers 官网 https://developers.google.com/protocol-buffers
-
下载 protobuf-csharp
-
解压后打开 csharp\src 中的 Google.Protobuf.sln
-
选择 Google.Protobuf 右键生成 dll 文件
-
在 csharp\src\Google.Protobuf\bin\Debug 路径下找到对应.net 版本的 Dll 文件(我们使用 4.5 即可)
-
将 net45 中的 dll 文件导入到 Unity 工程中的 Plugins 插件文件夹中
下载 Protobuf 相关内容 — 准备编译器
-
在官网中前往下载地址 protocol-buffers 官网 https://developers.google.com/protocol-buffers
-
下载 protoc - 版本 - win32 或者 64(根据操作系统而定)
-
解压后获取 bin 文件夹中的 protoc.exe 可执行文件,可将其放入 Unity 工程中,方便之后的使用(你也可以不放入 Unity 工程,记住它的路径即可)
创建proto结尾的配置文件
配置文件定义规则
//规则1:版本号
syntax = "proto3"; //版本号,不写默认使用proto2//规则2:注释方式
//注释方式1
/*注释方式2*///规则11:导入定义
import "test2.proto";//规则3:命名空间
package GamePlayerTest;//定义命名空间//规则4:消息类
message PlayerMsg{/*规则5:成员类型和唯一编号浮点数: float, double整数:变长编码- int32,int64,uint32,uint64固定字节数- fixed32,fixed64,sfixed32,sfixed64其他类型:bool,string,bytes唯一编号 配置成员时 需要默认给他们一个编号 从1开始这些编号用于标识中的字段消息二进制格式*/float testF = 1; //1不是float的默认值,而是唯一编号,方便序列化和反序列化的处理double testD = 2;int32 testInt32 = 3; //所谓边长就是根据数字的大小来使用对应的字节数来存储int64 testInt64 = 4;//sint跟适用于表示负数sint32 testSint32 = 5;sint64 testSint64 = 6;//uint无符号uint32 testUint32 = 7;uint64 testUint64 = 8;fixed32 testFixed32 = 9; //uint 通常用于表示大于2的28次方的数fixed64 testFixed64 = 10; //ulong 通常用于表示大于2的56次方的数sfixed32 testSfixed32 = 11; //intsfixed64 testSfixed64 = 12; //longbool testBool = 13;string testString = 14;bytes testBytes = 15; //BytesString 字节字符串/*规则6:特殊标识1:required 必须赋值的字段2:optional 可以不赋值的字段3:repeated 数组4:map 字典*/repeated int32 listInt32 = 16; //类似List<int>的使用map<int32,string> testMap = 17; //类型Dictionary<int,string>的使用//枚举成员变量的声明TestEnum testEnum = 18;//声明自定义类对象TestMsg2 testMsg2 = 19;//嵌套类 相当于内部类message TestMsg3{int32 testInt32 = 1;}//int32 tesInt22222222 = 20;//告诉编译器22被占用 不准用户使用//之所以有这个功能 是为了在版本不匹配时 反序列化时不会出现结构不统一解析错误的问题reserved 20;reserved "tesInt22222222";GameSystemTest.HeartMsg testHeart = 21;
}/*
规则7:枚举
enum 枚举名{常量1 = 0; //第一个常量必须映射到0常量2 = 1;}
*/
enum TestEnum{NORMAL = 0;BOSS = 5;PLAYER = 6;
}message TestMsg2{int32 testInt32 = 1;
}
利用 protoc.exe 编译器生成脚本文件
-
打开 cmd 窗口
-
进入 protoc.exe 所在文件夹(也可以直接将 exe 文件拖入 cmd 窗口中)
-
输入转换指令//protoc.exe -I = 配置路径 --csharp_out = 输出路径 配置文件名
注意:路径不要有中文和特殊符号,避免生成失败
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class lesson12 : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){GamePlayerTest.PlayerMsg player = new GamePlayerTest.PlayerMsg();player.TestF = 10.0f;player.TestEnum = GamePlayerTest.TestEnum.Player;player.ListInt32.Add(1);print(player.ListInt32[1]);player.TestMap.Add(1,"xxxxx");print(player.TestMap[1]);player.TestHeart = new GameSystemTest.HeartMsg();player.TestHeart.Time = 10;}// Update is called once per framevoid Update(){}
}
自动生成工具
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using UnityEditor;
using UnityEngine;public class ProtobufTool
{private static string PRPTP_PATH = "C:\\protobuf\\proto";private static string PROTO_PATH = "C:\\protobuf\\protoc.exe";private static string CSHARP_PATH = "C:\\protobuf\\csharp";[MenuItem("ProtocolBuf/生成C#代码")]private static void GenerateCSharp(){DirectoryInfo info = Directory.CreateDirectory(PRPTP_PATH);FileInfo[] fileInfos = info.GetFiles();for (int i = 0; i < fileInfos.Length; i++){if (fileInfos[i].Extension == ".proto"){ Process cmd = new Process();cmd.StartInfo.FileName = PROTO_PATH;cmd.StartInfo.Arguments = $"-I={PRPTP_PATH} --csharp_out={CSHARP_PATH} {fileInfos[i]}";cmd.Start();UnityEngine.Debug.Log(fileInfos[i] + "生成结束");}}}
}
序列化和反序列化
using Google.Protobuf;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;public class lesson12 : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){GamePlayerTest.PlayerMsg player = new GamePlayerTest.PlayerMsg();player.TestF = 10.0f;player.TestEnum = GamePlayerTest.TestEnum.Player;player.ListInt32.Add(1);print(player.ListInt32[1]);player.TestMap.Add(1,"xxxxx");print(player.TestMap[1]);player.TestHeart = new GameSystemTest.HeartMsg();player.TestHeart.Time = 10;//序列化using (FileStream fs = File.Create(Application.persistentDataPath + "/PlayerMsg.han")){player.WriteTo(fs);}//反序列化using (FileStream fs2 = File.OpenRead(Application.persistentDataPath + "/PlayerMsg.han")){GamePlayerTest.PlayerMsg playerMsg = GamePlayerTest.PlayerMsg.Parser.ParseFrom(fs2);}byte[] bytes = null;//得到序列化字节数组using (MemoryStream ms =new MemoryStream()){player.WriteTo(ms);bytes = ms.ToArray();}//从字节数组反序列化using (MemoryStream ms = new MemoryStream(bytes)){GamePlayerTest.PlayerMsg playerMsg = GamePlayerTest.PlayerMsg.Parser.ParseFrom(ms);}}// Update is called once per framevoid Update(){}
}