見出し画像

Unity Transport 入門(1) - シンプルなクライアント・サーバ間の通信

Unity Transport」を使って、シンプルなクライアント・サーバ間で通信する方法を説明します。

・Unity 2019.3.14f1
・Unity Transport 0.3.1

1. プロジェクトの準備

(1) Unityプロジェクトを「3D」テンプレートで作成。
(2) メニュー「Window → Package Manager」で「Package Manager」を開く。
(3) ウィンドウ上部の「Advances → show preview packages」をチェック。
(4) 「Unity Transport」(0.3.1)を検索してインストール。

2. 今回のサンプル

今回のサンプルは、クライアントがサーバに接続し、番号を送信すると、この番号はサーバによって受信され、別の番号が追加されてクライアントに返送されます。クライアントは、番号を受信すると、接続を切断して終了します。

3. シーンの準備

Hierarchyウィンドウの「+ → Create Empty」で空のゲームオブジェクトを作成し、名前に「Sample」を指定します。

画像1

4. サーバーの生成

「Sample」にスクリプト「Server​Behaviour」を追加し、以下のように編集します。

using UnityEngine;
using UnityEngine.Assertions;
using Unity.Collections;
using Unity.Networking.Transport;

public class ServerBehaviour : MonoBehaviour
{
    private NetworkDriver driver; // ドライバ
    private NativeList<NetworkConnection> connections; // 接続情報リスト

    // スタート時に呼ばれる
    void Start ()
    {
        // ドライバの生成
        this.driver = NetworkDriver.Create();

        // エンドポイントの生成
        var endpoint = NetworkEndPoint.AnyIpv4;
        endpoint.Port = 9000;
       
        // ドライバのバインド
        if (this.driver.Bind(endpoint) != 0) 
        {
            Debug.Log("ポート9000のバインド失敗");
        } 
        else 
        {
            // クライアントからの接続を待つ
            this.driver.Listen();
        }

        // 接続情報リストの生成
        this.connections = new NativeList<NetworkConnection>(16, Allocator.Persistent);
    }

    // 破棄時に呼ばれる
    public void OnDestroy()
    {
        // ドライバの破棄
        this.driver.Dispose();
       
        // 接続情報リストの破棄
        this.connections.Dispose();
    }

    // 1フレーム毎に呼ばれる
    void Update ()
    {
        // 通信Jobの実行
        this.driver.ScheduleUpdate().Complete();

        // 接続情報の削除
        for (int i = 0; i < this.connections.Length; i++)
        {
            if (!this.connections[i].IsCreated)
            {
                this.connections.RemoveAtSwapBack(i);
                --i;
            }
        }

        // 接続情報の追加
        NetworkConnection c;
        while ((c = this.driver.Accept()) != default(NetworkConnection))
        {
            this.connections.Add(c);
        }

        // 接続情報の反復
        DataStreamReader stream;
        for (int i = 0; i < this.connections.Length; i++)
        {
            Assert.IsTrue(this.connections[i].IsCreated);
 
            // イベントの反復
            NetworkEvent.Type cmd;
            while ((cmd = this.driver.PopEventForConnection(this.connections[i], out stream)) != NetworkEvent.Type.Empty)
            {
                // データ受信
                if (cmd == NetworkEvent.Type.Data)
                {
                    // データ受信
                    uint value = stream.ReadUInt();
                    Debug.Log("サーバがクライアントから値" + value + "を受信");
 
                    // データ送信
                    value += 2;
                    var writer = this.driver.BeginSend(NetworkPipeline.Null, this.connections[i]);
                    writer.WriteUInt(value);
                    this.driver.EndSend(writer);
                    Debug.Log("サーバがクライアントに値" + value + "を送信");
                }
                // 切断
                else if (cmd == NetworkEvent.Type.Disconnect)
                {
                    Debug.Log("サーバがクライアントと切断");
                    this.connections[i] = default(NetworkConnection);
                }
            }
        }
    }
}

◎  NetworkEndPoint
NetworkEndPoint」は、アドレスとポート番号を保持するエンドポイントです。

static NetworkEndPoint AnyIpv4 : サーバのエンドポイントの生成
static NetworkEndPoint LoopbackIpv4 : 接続先のエンドポイントの生成
ushort Port : ポート
string Address : アドレス

◎ NetworkDriver
「NetworkDriver」は接続するためのドライバです。
主なメソッドは次のとおりです。

◎ 接続
static NetworkDriver Create(params INetworkParameter[] param) : ドライバの生成

◎ クライアント接続
NetworkConnection Connect(NetworkEndPoint endpoint) : サーバーに接続

◎ サーバー接続
int Bind(NetworkEndPoint endpoint) : ドライバのバインド
int Listen() : クライアントからの接続を待つ
NetworkConnection Accept() : 接続情報の取得

◎ データ送信
DataStreamWriter BeginSend(NetworkConnection id, int requiredPayloadSize = 0) : データの送信開始
DataStreamWriter BeginSend(NetworkPipeline pipe, NetworkConnection id, int requiredPayloadSize = 0) : データの送信開始
int EndSend(DataStreamWriter writer) : データの送信完了

◎ データ受信
NetworkEvent.Type PopEventForConnection(NetworkConnection connectionId, out DataStreamReader reader) : イベントのポップ

◎ Jobの実行
JobHandle ScheduleUpdate(JobHandle dep = null) : 通信Jobの実行

◎ 切断
void Dispose() : 切断

◎ NetworkConnection
「NetworkConnection」はクライアント接続時に取得する接続情報です。
主なプロパティとメソッドは次のとおりです。

bool IsCreated : 接続中かどうか
PopEvent(NetworkDriver, out DataStreamReader) : イベントのポップ

◎ NetworkEvent.Type
ネットワークイベント種別は、次のとおりです。

・NetworkEvent.Type.Connect : 接続
・NetworkEvent.Type.Data : データ受信
・NetworkEvent.Type.Disconnect : 切断 
・NetworkEvent.Type.Empty : 空

◎ Jobの実行
「NetworkDriver」の通信処理は、「C# Job System」で実行します。
Update()で以下のようにジョブを実行します。

m_Driver.ScheduleUpdate().Complete();

5. クライアントの生成

「Sample」にスクリプト「ClientBehaviour」を追加し、以下のように編集します。

using UnityEngine;
using Unity.Networking.Transport;

public class ClientBehaviour : MonoBehaviour
{
    private NetworkDriver driver; // ドライバ
    private NetworkConnection connection; // 接続
    private bool done; // 完了

    // スタート時に呼ばれる
    void Start()
    {
        // ドライバの生成
        this.driver = NetworkDriver.Create();

        // エンドポイントの生成
        var endpoint = NetworkEndPoint.LoopbackIpv4;
        endpoint.Port = 9000;
       
        // サーバーとの接続
        this.connection = this.driver.Connect(endpoint);
    }

    // 破棄時に呼ばれる
    public void OnDestroy()
    {
        // ドライバの破棄
        this.driver.Dispose();
    }

    // 1フレーム毎に呼ばれる
    void Update()
    {
        // Jobの実行
        this.driver.ScheduleUpdate().Complete();

        // 切断
        if (!this.connection.IsCreated)
        {
            return;
        }


        // イベントの反復
        DataStreamReader stream;
        NetworkEvent.Type cmd;
        while ((cmd = this.connection.PopEvent(this.driver, out stream)) != NetworkEvent.Type.Empty)
        {
            // 接続
            if (cmd == NetworkEvent.Type.Connect)
            {
                Debug.Log("クライアントがサーバと接続");
 
                // データの送信
                uint value = 1;
                Debug.Log("クライアントがサーバに値" + value + "を送信");
                var writer = this.driver.BeginSend(this.connection);
                writer.WriteUInt(value);
                this.driver.EndSend(writer);
            }
            // データの受信
            else if (cmd == NetworkEvent.Type.Data)
            {
                // データの受信
                uint value = stream.ReadUInt();
                Debug.Log("クライアントがサーバから値" + value + "を受信");

                // 切断の実行
                this.done = true;
                this.connection.Disconnect(this.driver);
                this.connection = default(NetworkConnection);
            }
            // 切断
            else if (cmd == NetworkEvent.Type.Disconnect)
            {
                // 切断の完了
                Debug.Log("クライアントがサーバと切断");
                this.connection = default(NetworkConnection);
            }
        }
    }
}

6. 実行

コンソールに以下のように結果が表示されます。

画像2



この記事が気に入ったらサポートをしてみませんか?