导航

【Unity】protobuf故意踩坑记

Posted on 2020-11-28 21:00  Caiger  阅读(1551)  评论(0编辑  收藏  举报

写在前面

  • 起因:我看到工作项目使用protobuf来做序列化时脑子冒出许多问号,“以前我按《Unity3D网络游戏实战》做坦克游戏时为了让客户端和服务器使用统一协议用到了protobuf,怎么没有啥序列化的印象?”,“为什么非得选protobuf而不是其他工具?”,“序列化时常看到的MemoryStream和FileStream,以及StreamWriter、StreamReader这些到底是什么关系?”等等等等,于是我决定在自己的测试项目上从头开始摸索protobuf,以能成功用上protobuf为最终目标,顺便解决我这一系列问题。
  • 本篇博文主要记录我用头开始使用protobuf遇到的种种状况和问题解答,重点在踩坑而非如何使用protobuf。如何使用protobuf部分这里可以概括为:其实项目使用的是protobuf-net而非protobuf,从该谷歌网页下载protobuf-net r668压缩包,解压文件后使用里面的protobuf-net.dll和protogen.exe。
  • 本篇博文按“我的问题是什么”——>“解决问题过程记录”来组织。
  • Unity版本:2017.4.39f1

踩坑

  • protobuf是什么?——> 我先去翻了翻官网,官网首页说它是一个“无关语言无关平台且可扩展的序列化工具”,但这简单一句话并不能让我体会它的好,于是我去翻了翻博客们但也没找到更具体一点的解释,最后我还是回到了官网找到了具体解释(在官网Guides - Tutorials - Why use protocol buffers?),简单来说:它以我们会想到的几种序列化实现方法举例说明有哪些麻烦,而protobuf搞定了这些麻烦。
  • 为什么非得选protobuf而不是其他工具?——> 在主程的博客上,我看到了他选protobuf的原因,简单来说:他考虑过Unity自带的scriptObject和挂脚本,但该方法不支持热更;考虑过Xml,但它不支持Unity一些属性(如Color)序列化,而且影响重构(例如重构时改变量名是很常见的一件事),所以最后选用protobuf。
  • 怎么用protobuf序列化?——> (这里开始我在个人测试项目上操作,遇到状况颇多)
    • 我看了官网Tutorials,再看了它Github首页上的readme,它要安两个东西,一个是编译器protoc(其实最后要用的是protoc.exe),它负责把.proto文件生成对应.cs文件;另一个是dll,它负责代码实际功能(编码解码、序列化等)。但项目里用的不是谷歌官方的,而是第三方工具protobuf-net(当然它也是基于protobuf做的)。
    • 翻到的博客很多都没写到我关注的重点,有的把protobuf-net和protobuf混为一谈,有的不介绍为什么用protobuf-net而不是protobuf,而且protobuf安装说明总得有参考官方英文说明吧,也没列出参考,就直接讲如何安装了。
    • 我的项目Unity版本是2017.4.39f1,它用的是.net 3.5(如下图),但protobuf不支持.net 3.5(这在它Github上源码的readme文档里解释得很清楚,不过最开始我还是狂搜别人博客才知道大家不用官方protobuf的原因是这样),所以大家才选择了使用protobuf-net。

    • protobuf-net的Github上的What Files Do I Need文档里说,我会用到的是它的dll和protogen工具,因为我想直接拿到dll,所以直接在源码文件夹里搜protobuf-net.dll,发现有很多protobuf-net.dll都放在assorted文件夹下,而看这个文件夹的readme上作者说这个文件夹什么杂项啥都有。到底用哪个dll呢?作者的文件夹结构把我绕晕了……
    • 参考这篇博文,我打算编protobuf-net源码来得到它的dll,但工程用VS2015打开会有XML报错,放弃这个安装方向,转而参考这篇博文用NuGet来安装,发现protobuf-net最新版(此时最新版为3.0.62,它需要.net4.6.1).net版本比Unity要求高,而这里能支持.net3.5的protobuf-net最高版本就是2.4.6。

      NuGet帮我下在这了,但我并不知道哪些文件夹内容需要放Unity,这里也并没有protogen工具……

      于是我想去Github下源码看看能不能解决问题,我回到protobuf-net的Github上,用view all tag跳到release面板,查到能下的竟是2.4.5版本(它也没有日志说明支持.net版本)

点击release查看所有版本

      把2.4.5版本源码下载后,再打开工程仍有xml报错,查了解决方法要么在VS2015里一个个改错要么用VS2017,我决定去用VS2017。用VS2017后打开工程发现又有.net版本问题,我试图下了对应.net的SDK来解决但搞不定!于是回到前面NuGet下载protobuf-net2.4.6那步,这里的protobuf-net.dll我用的是net35文件夹里的dll,在Unity的Plugins文件夹下新建一个名为protobuf-net文件夹,将dll放入。我也试过放高于.net35版本的dll,显然在Unity里点dll时会看到报错。

      虽然NuGet可以帮下protobuf-net.dll,但protogen工具到底从哪里找呢?我试过用DotNetTool命令行工具下了从高到低好几个版本的protogen,但即使是NuGet上最低版本1.0.6的protogen它生成的.cs脚本放Unity里仍会有语法报错(用了C#6内容,当前Unity是C#4.0),而语法报错解决方法是Unity要升到.net4.6才行……

      我试过用G站上的protobuf-net2.4.5里的protogen工程生成protogen工具,也仍有语法报错。最后参考这个博文,我还是回到谷歌网页下了protobuf-net r668版本,直接用里面的protogen.exe(这里我也试过用G站上的protobuf-net r668源码生成protogen.exe,这次生成竟然成功了!)

      虽然之前我用NuGet下的protobuf-net2.4.6版本的protobuf-net.dll放Unity里没问题,但为了配合protobuf-netr668版本,最后还是用protobuf-netr668文件夹里的protobuf-net.dll。

    • 总的来说,我从谷歌网页下载protobuf-net r668,使用了里面的protobuf-net.dll和protogen.exe。
      • 在protogen.exe文件夹下随便建个.bat文件,内容如下图这么写,然后运行该bat,就能看到protogen使用帮助。

protogen帮助说明

        下图是执行protogen正确写法示例:

  • protobuf-net安好了,怎么用它的序列化呢?——> 说明就在protobuf-net的Github首页上,特性很少,使用极其简单。
  • 序列化时常看到的MemoryStream和FileStream,以及StreamWriter、StreamReader这些到底是什么关系?——>
    • 对于Stream,《深入理解C#》和《C#图解教程》、《CLR via C#》都没有讲,连基本IO读写都没讲。
    • 资料来自《精通C#》,以下内容是我的概括:
      • File和FileInfo关系:两者用哪个都行,不过用File能省更多代码,而用FileInfo会能得到更多文件信息。同理Directory和DirectoryInfo。
      • FileStream只能处理byte(读byte写byte),当然文档里仍然是正常显示(内容是什么显示就是什么)。
      • StreamWriter、StreamReader基于字符操作,就是用来WriteLine(…)这些,StringWriter意思差不多,只是和StreamWriter内部细节不太一样。
      • BinaryWriter基于二进制操作,文档里是二进制(就是看上去是乱码)。
      • 最后书上没讲MemoryStream内存流,只提了一句要保存为文件就用FileStream,放内存就用MemoryStream……我在msdn上查了MemoryStream,它说序列化用它性能更好,我在谷歌上没搜到“使用MemoryStream性能更好”的好举例。

  • 以前我按《Unity3D网络游戏实战》做坦克游戏时为了让客户端和服务器使用统一协议用到了protobuf,怎么没有啥序列化的印象?——>我回头去看书和做的工程了,它实际上用的就是protobuf-net工具,协议就是编码(序列化)后才收发的。书作者简单介绍了两句protobuf和protobuf-net的关系,直接给了protobuf-net的dll和protogen。