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」を指定します。
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. 実行
コンソールに以下のように結果が表示されます。
この記事が気に入ったらサポートをしてみませんか?