找回密码
 立即注册
查看: 242|回复: 0

Unity中使用ProtoBuf-保姆式教程

[复制链接]
发表于 2022-6-15 08:21 | 显示全部楼层 |阅读模式
·ProtoBuf介绍

ProtoBuf 是结构数据序列化方法,可简单类比于 XML、JSON,其具有以下特点:
    语言无关、平台无关。即 ProtoBuf 支持 Java、C++、Python 等多种语言,支持多个平台高效。即比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单扩展性、兼容性好。你可以更新数据结构,而不影响和破坏原有的旧程序


·ProtoBuf获取

我这边选择的版本为 ProtoBuf 3.5.x
一共需要两个步骤
1.Google.Protobuf

从GitHub上获取完整版 传送门, 选择3.5.x的版本


这里有2条方式可供选择
(1)在unity中使用源码


Google.Protobuf这个文件夹直接放入到unity中
(2)在unity中使用Google.Protobuf.dll


使用vs打开它,用release去编译Google.Protobuf这个工程,在release这个目录下你会得到这个dll,放入到你的Libraries中
2.Protoc

从GitHub上获取windows版本 传送门, 选择3.5.1的版本


在bin中找到我们要的protoc.exe
写好 proto 文件之后用 protoc 编译器将 .proto文件编译成目标语言。




·.proto文件

测试文件内容
  1. // 指定版本
  2. syntax = "proto3";
  3. // C#中的namespace
  4. package ProtoTest
  5. option optimize_for = SPEED;
  6. // java文件路径
  7. option java_package = "com.montior.proto";
  8. // java文件名称
  9. option java_outer_classname = "MonitorData";
  10. // 消息结果。
  11. message MsgResult {
  12.     // 结果码。
  13.     int32 code = 1;
  14.     // 错误消息。
  15.     string err_msg = 2;
  16. }
  17. // 接收包
  18. message TaskProtocol {
  19.     // 数据类型
  20.     int32 packType = 1;
  21.     // 具体数据
  22.     bytes content = 3;
  23. }
  24. // 包的类型
  25. enum PackType {
  26.     LOGIN = 0;
  27.     CREATE_TASK = 2;
  28.     DELETE_TASK = 3;
  29. }
  30. message LoginPack{
  31.     string username = 1;
  32. }
  33. message LoginPack2{
  34.     string username = 1;
  35. }
  36. message CreateTaskPack{
  37.     string taskId = 1;
  38.     string taskName = 2;
  39. }
复制代码
    message:消息类型,类似于一个类
    package:包名,CSharp中的命名空间,用来防止不同消息类型的冲突
    enum:枚举,这个需要我说吗?
    option:选项,说明下我这边用到的
    option java_package = “com.example.foo”;// java文件路径
    option java_outer_classname = “Ponycopter”;// java文件名称
    option optimize_for = SPEED;//可以被设置为 SPEED, CODE_SIZE,or LITE_RUNTIME。这些值将通过如下的方式影响C++及java代码的生成:
    注:以上选项,CSharp都用不着的,就是写着玩儿....
  • 数据类型   
    protobuf 数据类型
    描述
    C++
    bool
    布尔类型
    bool
    double
    64位浮点数
    double
    float
    32为浮点数
    float
    int32
    32位整数、
    int
    uin32
    无符号32位整数
    unsigned int
    int64
    64位整数
    __int64
    uint64
    64为无符号整
    unsigned __int64
    sint32
    32位整数,处理负数效率更高
    int32
    sing64
    64位整数 处理负数效率更高
    __int64
    fixed32
    32位无符号整数
    unsigned int32
    fixed64
    64位无符号整数
    unsigned __int64
    sfixed32
    32位整数、能以更高的效率处理负数
    unsigned int32
    sfixed64
    64为整数
    unsigned __int64
    string
    只能处理 ASCII字符
    std::string
    bytes
    用于处理多字节的语言字符、如中文
    std::string

  • 关键字   
    指定字段说明
    required表示是一个必须字段,必须相对于发送方,在发送消息之前必须设置该字段的值,对于接收方,必须能够识别该字段的意思。发送之前没有设置required字段或者无法识别required字段都会引发编解码异常,导致消息被丢弃。
    optional表示是一个可选字段,可选对于发送方,在发送消息时,可以有选择性的设置或者不设置该字段的值。对于接收方,如果能够识别可选字段就进行相应的处理,如果无法识别,则忽略该字段,消息中的其它字段正常处理。---因为optional字段的特性,很多接口在升级版本中都把后来添加的字段都统一的设置为optional字段,这样老的版本无需升级程序也可以正常的与新的软件进行通信,只不过新的字段无法识别而已,因为并不是每个节点都需要新的功能,因此可以做到按需升级和平滑过渡。
    repeated表示该字段可以包含0~N个元素。其特性和optional一样,但是每一次可以包含多个值。可以看作是在传递一个数组的值。



·在Unity的布局

UnityProject
         -Asset
                 --Libraries
                         ---Google.Protobuf        // Protobuf源文件/dll文件
                --Scripts/Editor/Proto2CSEditor        // 放置.proto文件转化为cs的代码
                 --Scripts/ProtoMessage        // 放置转化的cs的代码
????        -Proto
                 --monitorData.proto        //放置.proto文件
                 --protoc.exe                    //
·如何使用protoc.exe把.proto文件转化为.cs
  1.         [MenuItem("Tools/Proto2CS")]
  2.         public static void AllProto2CS()
  3.         {
  4.             string rootDir = Environment.CurrentDirectory;
  5.             string protoDir = Path.Combine(rootDir, "Proto/");
  6.             string protoc;
  7.             if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
  8.             {
  9.                 protoc = Path.Combine(protoDir, "protoc.exe");
  10.             }
  11.             else
  12.             {
  13.                 protoc = Path.Combine(protoDir, "protoc");
  14.             }
  15.             string hotfixMessageCodePath = Path.Combine(rootDir, "Assets", "Scripts", "ProtoMessage/");
  16.             string argument2 = $"--csharp_out="{hotfixMessageCodePath}" --proto_path="{protoDir}" monitorData.proto";
  17.             Run(protoc, argument2, waitExit: true);
  18.             UnityEngine.Debug.Log("proto2cs succeed!");
  19.             AssetDatabase.Refresh();
  20.         }
  21.         public static Process Run(string exe, string arguments, string workingDirectory = ".", bool waitExit = false)
  22.         {
  23.             try
  24.             {
  25.                 bool redirectStandardOutput = true;
  26.                 bool redirectStandardError = true;
  27.                 bool useShellExecute = false;
  28.                 if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
  29.                 {
  30.                     redirectStandardOutput = false;
  31.                     redirectStandardError = false;
  32.                     useShellExecute = true;
  33.                 }
  34.                 if (waitExit)
  35.                 {
  36.                     redirectStandardOutput = true;
  37.                     redirectStandardError = true;
  38.                     useShellExecute = false;
  39.                 }
  40.                
  41.                 ProcessStartInfo info = new ProcessStartInfo
  42.                 {
  43.                     FileName = exe,
  44.                     Arguments = arguments,
  45.                     CreateNoWindow = true,
  46.                     UseShellExecute = useShellExecute,
  47.                     WorkingDirectory = workingDirectory,
  48.                     RedirectStandardOutput = redirectStandardOutput,
  49.                     RedirectStandardError = redirectStandardError,
  50.                 };
  51.                
  52.                 Process process = Process.Start(info);
  53.                 if (waitExit)
  54.                 {
  55.                     process.WaitForExit();
  56.                     if (process.ExitCode != 0)
  57.                     {
  58.                         throw new Exception($"{process.StandardOutput.ReadToEnd()} {process.StandardError.ReadToEnd()}");
  59.                     }
  60.                 }
  61.                 return process;
  62.             }
  63.             catch (Exception e)
  64.             {
  65.                 throw new Exception($"dir: {Path.GetFullPath(workingDirectory)}, command: {exe} {arguments}", e);
  66.             }
  67.         }
复制代码
·序列化与反序列化



·举个栗子
  1.         MsgResult result = new MsgResult
  2.         {
  3.             Code = -999,
  4.             ErrMsg = "Error"
  5.         };
  6.         TaskProtocol msgResult = new TaskProtocol
  7.         {
  8.             PackType = 111,
  9.             Content = result.ToByteString()
  10.         };
  11.         byte[] s = packer.SerializeTo(msgResult);
  12.         Debug.Log("---------------------------------------------------");
  13.         TaskProtocol response = new TaskProtocol();
  14.         packer.DeserializeFrom(response, s);
  15.         MsgResult responseMsgResult = new MsgResult();
  16.         packer.DeserializeFrom(responseMsgResult, response.Content.ToByteArray());
  17.         Debug.Log(response.PackType);
  18.         Debug.Log(responseMsgResult.Code);
  19.         Debug.Log(responseMsgResult.ErrMsg);
复制代码
·Unity中还可以使用protobuf-net.dll

<!--暂无-->

·结语

如果这样你都还配置不好ProtoBuf,不知道怎么序列化和反序列化的话!!!!
那就多看几遍,哈哈哈

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-26 14:39 , Processed in 0.065764 second(s), 23 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表