こんばんは。組込みエンジニアの人です。
趣味で、Unityにて、MO系のゲームを作ろうとした際に、ProtocolBuffersを採用しようと思ったのですが、環境構築に嵌ってしまったのと、まとまった情報が無かったので、メモ書き程度に残します。
Protocol Buffersとは
下記、記事が分かりやすかったです。本ページでは説明を省略します。
ProtocolBuffersについて調べてみた
なぜ、バイナリデータを使うか?
オンラインゲームでは、特に短い間隔でソケット通信を行うとします。
例えば、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を使用しました。
解凍をすると、protobuf-net.2.4.6\lib\net20の中に、protobuf-net.dllがあると思います。
2. .protoファイルのコンパイル
下記のように.protoファイルを作ります。
文法については、公式サイトを参照お願いします。
syntax = "proto3";
package moprotocol;
message MOProtocol {
string msgID = 1;
map<string, string> data = 2;
}
コンパイルについては、protogen.exeを使うことも出来るのですが、ブラウザ上で、コンパイルできるサービスを見つけました。
https://protogen.marcgravell.com/
protogenのC#を選択した状態で、上記のコードをコピーし、Generateボタンを押してください。
下の画面上に、ソースコードが出てきます。それをUnity上で使うので、MOProtocol.csとして保存してください。
3.Unity上にライブラリを入れる。
Unityを起動し、適当にプロジェクトを起動してください。
最初にprotobuf-net.dllをAssets/Plugins上に入れてください。
次に、先ほど作成したMOProtocol.csを任意のフォルダに入れます。今回は、Assets/Scripts/ProtocolBufに入れます。
最後に、Unity上で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を任意のオブジェクトに割付け実行すると下記のログが出ます。
ログ上にシリアライズした結果、デシリアライズした結果が出てくれば成功です。
おわりに
有識者ではなく、勉強中の身であるため、間違い等あれば、ご指摘等頂ければ幸いです。
なお、本ページとは話が脱線しますが、書籍のオンラインゲームを支える技術にて、バイナリデータの必要性が説明されており、導入に至りました。
この書籍は、分かりやすい上に、深い内容まで言及されているので、オンラインゲームを作るときの参考材料にもってこいです。
参考サイト
gRPCとProtocol Buffersによるアプリケーション間通信 / Unity
[Protocol Buffer Basics: C#]
(https://developers.google.com/protocol-buffers/docs/csharptutorial)