UnityでProtocolBuffersを使ってみましょう。

こんばんは。組込みエンジニアの人です。
趣味で、Unityにて、MO系のゲームを作ろうとした際に、ProtocolBuffersを採用しようと思ったのですが、環境構築に嵌ってしまったのと、まとまった情報が無かったので、メモ書き程度に残します。

## Protocol Buffersとは

下記、記事が分かりやすかったです。本ページでは説明を省略します。
[ProtocolBuffersについて調べてみた](https://qiita.com/aiueo4u/items/54dc5dd8c4772253634c)

## なぜ、バイナリデータを使うか？

オンラインゲームでは、特に短い間隔でソケット通信を行うとします。
例えば、10[ms]感覚で256[byte]のテキストデータを送るとします。

クライアント⇒サーバへ1[s]送る回線データは、256[byte] * ( 1000[ms] / 10[ms]) = 256,000[byte]とします。

これがクライアントが100台あった場合、256,000[byte] * 100[台] = 256,000,000[byte]
つまり、1秒間に256[MB]の回線データが飛ぶことになります。
サーバ⇒クライアントから返すデータ等を加味するともっと増えますね。

Protocol-Buffersは、テキストデータをバイナリデータに変換することで、データのサイズを圧縮することが可能です。
例えば、先ほどの例のテキストデータを圧縮することで128[byte]になったのであれば、それだけで使用する回線データは半分に削減できます。

こうすることで、変換する処理はクライアント⇔サーバで負荷は増えるのですが、通信帯域を圧迫せずに済みます。


## ProtoBuf-netとGoogle.ProtoBufの違い
実装を進めていく中で、2つC#のライブラリがあることが分かりました。
ざっくりと説明すると、
protobuf-netは、ProtocolBuffers の非公式 C# 実装。
Google.ProtoBufは、ProtocolBuffers の公式 C# 実装。
です。

Google.ProtoBufは、依存関係が複雑なのか、単体で、.dllを読込むとエラーとなるため、
今回は、protobuf-netを使います。ProtocolBuffersの規格は決まっているおり、
圧縮したいという目的は達成できるため、どちらでも、よいと判断しました。

## Unityに導入する手順

### 1. proto-buf-netのダウンロード
下記のサイトより、Download packageにて、ダウンロードします。
拡張子が.nugetになっているので、.zipに直して、解凍してください。
執筆時のバージョンは、2.4.6を使用しました。

https://www.nuget.org/packages/protobuf-net/

解凍をすると、protobuf-net.2.4.6\lib\net20の中に、protobuf-net.dllがあると思います。

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605728/ef9cff83-3180-f976-9aa1-be8bc2e44ae1.png)


### 2. .protoファイルのコンパイル
下記のように.protoファイルを作ります。
文法については、[公式サイト](https://developers.google.com/protocol-buffers)を参照お願いします。

```proto:example.proto
syntax = "proto3";
package moprotocol;

message MOProtocol {
    string msgID = 1;
    map<string, string> data = 2;
}
```

コンパイルについては、protogen.exeを使うことも出来るのですが、ブラウザ上で、コンパイルできるサービスを見つけました。
https://protogen.marcgravell.com/

protogenのC#を選択した状態で、上記のコードをコピーし、Generateボタンを押してください。

![キャプチャ２.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605728/d0a6502d-3d43-d8c1-0ed3-73689f17809e.png)

下の画面上に、ソースコードが出てきます。それをUnity上で使うので、MOProtocol.csとして保存してください。
![キャプチャ３.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605728/b9081c91-77e5-dfe7-7cd8-18644fb77eaa.png)

### 3.Unity上にライブラリを入れる。
Unityを起動し、適当にプロジェクトを起動してください。

最初にprotobuf-net.dllをAssets/Plugins上に入れてください。
![キャプチャ４.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605728/e2ffa38d-912c-cbd1-bc1a-ef0dd020ada7.png)

次に、先ほど作成したMOProtocol.csを任意のフォルダに入れます。今回は、Assets/Scripts/ProtocolBufに入れます。
![キャプチャ5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605728/e31586d5-2911-1a55-44fc-e0d6b2068a7c.png)

最後に、Unity上でProtocolManager.csを作ってください。
(サンプルコードなので、名前は任意でよいです。)
以下にサンプルコードを示します。

```csharp:ProtocolManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.IO;

using ProtoBuf;
using Moprotocol;

public class ProtocolManager : MonoBehaviour
{
    MOProtocol protocolSerialize;
    MOProtocol protocolDeSerialize;
    // Start is called before the first frame update
    void Start()
    {
        protocolSerialize = new MOProtocol();

        protocolSerialize.msgID = "999999";

        protocolSerialize.Datas.Add("テスト1", "aaaaa");
        protocolSerialize.Datas.Add("テスト2", "bbbbb");

        using (var ms = new MemoryStream())
        {
            Serializer.Serialize(ms, protocolSerialize);
            byte[] bytes = ms.ToArray();
            //シリアライズ(デバッグ用途)
            Debug.Log(BitConverter.ToString(bytes));

            //デシリアライズ(デバッグ用途)
            var ms2 = new MemoryStream(bytes);
            protocolDeSerialize = Serializer.Deserialize<MOProtocol>(ms2);
            foreach (string Value in protocolDeSerialize.Datas.Values)
            {
                Debug.Log(Value);
            }
            Debug.Log(protocolDeSerialize.msgID);
        }
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}
```

上記のProtocolManager.csを任意のオブジェクトに割付け実行すると下記のログが出ます。
ログ上にシリアライズした結果、デシリアライズした結果が出てくれば成功です。

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605728/c9844661-5e06-6336-7bd0-ce65f38786a2.png)

## おわりに
有識者ではなく、勉強中の身であるため、間違い等あれば、ご指摘等頂ければ幸いです。

なお、本ページとは話が脱線しますが、書籍の[オンラインゲームを支える技術](https://www.amazon.co.jp/%E3%82%AA%E3%83%B3%E3%83%A9%E3%82%A4%E3%83%B3%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%92%E6%94%AF%E3%81%88%E3%82%8B%E6%8A%80%E8%A1%93-%E2%80%95%E5%A3%AE%E5%A4%A7%E3%81%AA%E3%83%97%E3%83%AC%E3%82%A4%E7%A9%BA%E9%96%93%E3%81%AE%E8%88%9E%E5%8F%B0%E8%A3%8F-WEB-PRESS-plus-ebook/dp/B07JJH8W8M)にて、バイナリデータの必要性が説明されており、導入に至りました。
この書籍は、分かりやすい上に、深い内容まで言及されているので、オンラインゲームを作るときの参考材料にもってこいです。

## 参考サイト
[gRPCとProtocol Buffersによるアプリケーション間通信 / Unity](https://note.com/npaka/n/n050b1aba7772)

[C#でProtocolBuffersを使う方法](https://kagasu.hatenablog.com/entry/2018/04/29/233407#2-proto%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B-1)

[Protocol Buffer Basics: C#]
(https://developers.google.com/protocol-buffers/docs/csharptutorial)