見出し画像

Unity Transport 入門(2) - Job化したクライアント・サーバ間の通信

「Unity Transport」を使って、Job化したクライアント・サーバ間で通信する方法を説明します。

・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)を検索してインストール。
(5) 「Jobs」(0.2.10)を検索してインストール。

2. シーンの準備

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

画像1

3. サーバーの生成

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

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

struct ServerUpdateConnectionsJob : IJob
{
    public NetworkDriver driver; // ドライバ
    public NativeList<NetworkConnection> connections; // 接続情報リスト

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

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

struct ServerUpdateJob : IJobParallelForDefer
{
    public NetworkDriver.Concurrent driver; // ドライバ
    public NativeArray<NetworkConnection> connections; // 接続情報リスト

    // Jobの実行
    public void Execute(int index)
    {
        // イベントの反復
        DataStreamReader stream;
        NetworkEvent.Type cmd;
        while ((cmd = this.driver.PopEventForConnection(this.connections[index], out stream)) != NetworkEvent.Type.Empty)
        {
            // データ受信
            if (cmd == NetworkEvent.Type.Data)
            {
                // データ受信
                uint value = stream.ReadUInt();
                Debug.Log("サーバがクライアントから値" + value + "を受信");

                // データ送信
                value += 2;
                var writer = this.driver.BeginSend(this.connections[index]);
                writer.WriteUInt(value);
                this.driver.EndSend(writer);
                Debug.Log("サーバがクライアントに値" + value + "を送信");
            }
            // 切断
            else if (cmd == NetworkEvent.Type.Disconnect)
            {
                Debug.Log("サーバがクライアントと切断");
                this.connections[index] = default(NetworkConnection);
            }
        }
    }
}

public class JobifiedServerBehaviour : MonoBehaviour
{
    private NetworkDriver driver; // ドライバ
    private NativeList<NetworkConnection> connections; // 接続情報リスト
    private JobHandle serverJobHandle; // Jobハンドル

    // スタート時に呼ばれる
    void Start ()
    {
        // 接続情報リストの生成
        this.connections = new NativeList<NetworkConnection>(16, Allocator.Persistent);

        // ドライバの生成
        this.driver = NetworkDriver.Create();

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

    // 破棄時に呼ばれる
    public void OnDestroy()
    {
        // 通信Jobの完了待ち
        this.serverJobHandle.Complete();
       
        // 接続情報リストの破棄
        this.connections.Dispose();
       
        // ドライバの破棄
        this.driver.Dispose();
    }

    // 1フレーム毎に呼ばれる
    void Update ()
    {
        // Jobの完了待ち
        this.serverJobHandle.Complete();

        // 接続Jobの生成
        var connectionJob = new ServerUpdateConnectionsJob
        {
            driver = this.driver,
            connections = this.connections
        };

        // 更新Jobの生成
        var serverUpdateJob = new ServerUpdateJob
        {
            driver = this.driver.ToConcurrent(),
            connections = this.connections.AsDeferredJobArray()
        };

        // Jobのスケジュール
        this.serverJobHandle = this.driver.ScheduleUpdate();
        this.serverJobHandle = connectionJob.Schedule(this.serverJobHandle);
        this.serverJobHandle = serverUpdateJob.Schedule(this.connections, 1, this.serverJobHandle);
    }
}

4. クライアントの生成

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

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

struct ClientUpdateJob : IJob
{
    public NetworkDriver driver; // ドライバ
    public NativeArray<NetworkConnection> connection; // 接続情報リスト
    public NativeArray<byte> done; // 完了

    // Jobの実行
    public void Execute()
    {
        // 切断
        if (!this.connection[0].IsCreated)
        {
            return;
        }

        // イベントの反復
        DataStreamReader stream;
        NetworkEvent.Type cmd;
        while ((cmd = this.connection[0].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[0]);
                writer.WriteUInt(value);
                this.driver.EndSend(writer);
            }
            // データ受信
            else if (cmd == NetworkEvent.Type.Data)
            {
                // データ受信
                uint value = stream.ReadUInt();
                Debug.Log("クライアントがサーバから値" + value + "を受信");

                // 切断
                this.done[0] = 1;
                this.connection[0].Disconnect(driver);
                this.connection[0] = default(NetworkConnection);
            }
            // 切断
            else if (cmd == NetworkEvent.Type.Disconnect)
            {
                Debug.Log("クライアントがサーバと切断");
               this.connection[0] = default(NetworkConnection);
            }
        }
    }
}

public class JobifiedClientBehaviour : MonoBehaviour
{
    public NetworkDriver driver; // ドライバ
    public NativeArray<NetworkConnection> connection; // 接続情報リスト
    public NativeArray<byte> done; // 完了
    public JobHandle clientJobHandle; // Jobハンドル

    // スタート時に呼ばれる
    void Start ()
    {
        // ドライバの生成
        this.driver = NetworkDriver.Create();
 
        // 接続情報リストの生成
        this.connection = new NativeArray<NetworkConnection>(1, Allocator.Persistent);

        // 完了の生成
        this.done = new NativeArray<byte>(1, Allocator.Persistent);

        // 接続
        var endpoint = NetworkEndPoint.LoopbackIpv4;
        endpoint.Port = 9000;
        this.connection[0] = this.driver.Connect(endpoint);
    }

    // 破棄時に呼ばれる
    public void OnDestroy()
    {
        // Jobの完了待ち
        this.clientJobHandle.Complete();
        
        // 接続情報の破棄
        this.connection.Dispose();
        
        // ドライバの破棄
        this.driver.Dispose();
        
        // 完了の破棄
        this.done.Dispose();
    }

    // 1フレーム毎に呼ばれる
    void Update()
    {
        // Jobの完了待ち
        this.clientJobHandle.Complete();

        // 更新Jobの実行
        var job = new ClientUpdateJob
        {
            driver = this.driver,
            connection = this.connection,
            done = this.done
        };
        
        // Jobのスケジュール
        this.clientJobHandle = this.driver.ScheduleUpdate();
        this.clientJobHandle = job.Schedule(this.clientJobHandle);
    }
}

5. 実行

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

画像2



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