1. 程式人生 > >Protobuf-net在Unity中的序列化與反序列化

Protobuf-net在Unity中的序列化與反序列化

本篇中我們只講解如何在Unity中對Protobuf-net進行序列化(Serialize)與反序列化(Deserialize),關於Unity的socket網路通訊部分我們後續開篇。

首先去Protobuf-net的Google下載點下載protobuf-net類庫:
這裡用的是目前最新的protobuf-net r668
下載完畢後開啟壓縮包,在Full\unity中找到protobuf-net.dll將其新增到專案中。
接下來過程其實類似於我之前的一文章《Protobuf在Java中的簡單例項》

①建立proto檔案,定義協議格式
首先我們需要建立一個.proto檔案來進行測試:

1
2
3
4
5
package com.beitown.net.msg;//包名
message TestMsg {
    required int64 Id=1; 
    required string Name=2;
}

每個欄位後標記的“=1”、“=2”這裡就不解釋了,之前的一篇Protobuf文章中已經做過概述。

②編譯Proto檔案生成.cs檔案
這裡我們需要用到一個編譯工具ProtoGen,在之前下載的protobuf-net r668.zip中就有。
為了方便管理.proto和.cs檔案以及編譯快捷,我們可以寫一個bat指令碼來實現自動編譯.proto檔案,指令碼如下:

1
2
3
@echo off
for /"delims=" %%i in ('dir /b proto "proto/*.proto"') do protogen -i:proto/%%-o:cs/%%~ni.cs
pause


為了配合這個指令碼,需要在ProtoGen資料夾中另外再建立兩個子資料夾,一個proto資料夾,一個cs資料夾。批處理會自動尋找proto資料夾下的.proto檔案並編譯成相應.cs檔案儲存到cs目錄中。
ok,接下來將之前寫好的Test.proto檔案放到proto資料夾中,執行creat.bat指令碼。此時會在cs資料夾下生成一個名為Test.cs的檔案。
我們先來觀察一下這個Test.cs檔案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

// Generated from: proto/Test.proto
namespace com.beitown.net.msg
{
  [global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"TestMsg")]
  public partial class TestMsg : global::ProtoBuf.IExtensible
  {
    public TestMsg() {}
    
    private long _Id;
    [global::ProtoBuf.ProtoMember(1, IsRequired = true, Name=@"Id", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)]
    public long Id
    {
      get { return _Id; }
      set { _Id = value; }
    }
    private string _Name;
    [global::ProtoBuf.ProtoMember(2, IsRequired = true, Name=@"Name", DataFormat = global::ProtoBuf.DataFormat.Default)]
    public string Name
    {
      get { return _Name; }
      set { _Name = value; }
    }
    private global::ProtoBuf.IExtension extensionObject;
    global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
      { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); }
  }
  
}

和Protobuf-Csharp的編譯機制編譯出的cs檔案比起來要清晰簡單很多,同理和java端的編譯後文件比起來也非常清晰,這也是Protobuf-net的一個讓人覺得親切的地方,儘管自動生成的程式碼我們都不會再去手動修改甚至去瀏覽。
接下來將這個Test.cs檔案新增到專案中,即可對TestMsg這個protobuf結構進行操作了。

③序列化與反序列化
在傳送資料前我們需要將Protobuf結構進行序列化,在本例中即給之前定義的TestMsg結構體賦值,並將其轉換成二進位制形式方便通訊機制傳送。
這裡直接寫成了靜態方法。

i 序列化:

1
2
3
4
5
6
7
8
9
10
11
using ProtoBuf;
public static byte[] Serialize(IExtensible msg)
        {
            byte[] result;
            using (var stream = new MemoryStream())
            {
                Serializer.Serialize(stream, msg);
                result = stream.ToArray();
            }
            return result;
        }

IExtensible是ProtoBuf包下的一個公共介面,參考Test.cs的檔案結構(TestMsg : global::ProtoBuf.IExtensible)可以發現,在整個規則中所有的的protobuf結構都實現了ProtoBuf.IExtensible這個介面,因此也方面我們的封裝。

接下來是反序列化的封裝。
ii 反序列化:

1
2
3
4
5
6
7
8
9
10
using ProtoBuf;
public static IExtensible Deserialize<IExtensible>(byte[] message)
        {
            IExtensible result;
            using (var stream = new MemoryStream(message))
            {
                result = Serializer.Deserialize<IExtensible>(stream);
            }
            return result;
        }

封裝完畢之後我們來看看使用的方法,直接上程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Main : MonoBehaviour
{
    // Use this for initialization
    void Start()
    {
        //建立socket連線之類種種忽略...

        Testmsg protoOut= new Testmsg ();
        protoOut.Id = 10046;
        protoOut.name= "beitown";
        byte[] bytes = Serialize(protoOut);

        //socket.send(bytes)之類種種,傳送到位元組流中...

    }

    // Update is called once per frame
    void Update()
    {
        //當獲取到一個訊息在bytes中
        TestMsg protoIn = (TestMsg)Deserialize<TestMsg>(bytes);//強轉成TestMsg型別
        Debug.log("Id: " + protoIn.Id);//獲取Id
        Debug.log("Name: " + protoIn.Name);//獲取Name

    }
}

以上程式碼寫的略做精簡,不涉及任何通訊部分的描述,大家需要根據自己的情況來進行另外的封裝,這裡就不再描述了,後續可能會寫一篇UnitySocket封裝和智慧Command的文章,到時再做敘述。
參照本文中的內容再加上一個簡單的Socket通訊,即可完成一個簡單的Unity Protobuf-net的小Demo,感興趣的朋友可以繼續,望能拋磚引玉。
本篇到此,謝謝關注。