Multiplayer のロビー
NetworkReader と NetworkWriter シリアライザー

Transport Layer API の使用

高レベルネットワーク API (HLAPI) に加え、Transport Layer と呼ばれる低レベルネットワーク API へのアクセスを提供します。Transport Layer API を使用すると、ゲームのネットワークに特有の、または高度な要件を備えた独自のネットワークシステムを構築できます。

Transport Layer (トランスポート層) は、OS のソケットベースネットワークの上にある です。バイトの配列で表されるメッセージの送受信を行い、さまざまなケースに対応するために サービス品質 (QoS) を変更できるオプションを多数提供します。Transport Layer は柔軟性とパフォーマンスに重点を置いたもので、NetworkTransport クラス内の API でアクセス可能です。

Transport Layer API は、ネットワーク通信の基本的な機能に対応しています。これには以下が含まれます。

  • 接続の確立

  • 異なるサービス品質 (QoS) による通信

  • フロー制御

  • 基本的な統計情報

  • リレーサーバー経由の通信やローカルディスカバリーなどの付加的サービス

Transport Layer は 2 つのプロトコルを使用することができます。全般的な通信には UDP、WebGL には WebSockets が使用されます。Transport Layer を直接使用するための、一般的なワークフローは以下の通りです。

  1. Network Transport Layer の初期化

  2. ネットワークトポロジーの設定

  3. ホストの作成

  4. 通信 (接続およびメッセージの送受信の処理) の開始

  5. 使用後はライブラリを終了します。

技術的な詳細を学ぶには下の関連するセクションを参照してください。各セクションにネットワークスクリプトに使用できるコードスニペットがあります。

ステップ 1: Network Transport Layer の初期化

Network Transport Layer を初期化する場合、以下のコードサンプルで紹介されているデフォルトの初期化 (引数なし) にするか、あるいは、ネットワークレイヤーの全面的な挙動を制御する追加パラメーター (例えば、最大パケットサイズやスレッドのタイムアウト制限など) を提供するか、どちらかを選ぶことができます。

デフォルト設定で Transport Layer を初期化するには、Init() を呼び出します。


    // Transport Layer を引数無しで初期化 (デフォルト設定)
        NetworkTransport.Init();

    Transport Layer を独自の設定で初期化する場合は、下の例のように、単に設定をパラメーターとして Init に加えます。

        // カスタム設定で Transport Layer を初期化する例
        GlobalConfig gConfig = new GlobalConfig();
        gConfig.MaxPacketSize = 500;
        NetworkTransport.Init(gConfig);

カスタムの Init 値は、特殊なネットワーク環境を使用していて、必要な設定を明確に理解している場合にのみ使用してください。基本的には、インターネットを通してプレイする典型的なマルチプレイヤーゲームの開発の場合、デフォルト Init 設定が適切です。

ステップ 2: Network Transport Layer の設定

次のステップは、ピア間の接続の設定です。ネットワークトポロジーによって、接続可能数と使用する接続設定が決定されます。ゲームで重要性の異なるネットワークメッセージ (例えば、付随的なサウンドエフェクトなどの重要度の低いメッセージ vs プレイヤーがポイントを獲得したか否かなどの重要度の高いメッセージ) を送信する必要がある場合は、いくつかの通信チャンネルを定義する必要があります。各チャンネルのサービス品質レベルは、送信する特定の種類のメッセージや、ゲーム中におけるそれらの相対的な重要度に合うように設定します。


    ConnectionConfig config = new ConnectionConfig();
        int myReliableChannelId  = config.AddChannel(QosType.Reliable);
        int myUnreliableChannelId = config.AddChannel(QosType.Unreliable);

上記の例では、サービス品質レベルの値の異なる二つの通信チャンネルを定義しています。QosType.Reliable はメッセージを送信し、それが届いたかを確認します。一方、QosType.Unreliable はメッセージをより速く送信しますが、届いたかの確認はしません。

また、ConnectionConfig オブジェクトのプロパティーを調整することで、環境設定を各接続ごとに行うことも可能です。ただし、あるクライアントから他のクライアントへの接続を行う場合、両方のピアで設定を同じにしないと、CRCMismatch エラーで接続に失敗してしまいます。

ネットワーク設定の最後のステップは、トポロジー定義です。


HostTopology topology = new HostTopology(config, 10);

この例では、最大 10 の接続を許可するようにホストトポロジーを設定します。これらの接続はステップ 1 で設定したものです。

ステップ 3: ホストの作成

最初の 2 つの基本的な設定手順は終了したので、次にホスト (オープンソケット) を作成する必要があります。


int hostId = NetworkTransport.AddHost(topology, 8888);

このコード例では、新しいホストを (IP アドレスがあればそれもいっしょに) ポート 8888 に加えます。ホストは最大 10 の接続をサポートします (これはステップ 2 で設定しました)。これらの接続はステップ 1 で設定したものです。

ステップ 4: 通信開始

通信を開始するには、もう 1 つのホストへの接続を設定する必要があります。そのためには、Connect() を呼び出します。そうすることによって、自身とリモートホスト間の接続を設定します。接続が成功すると、任意のイベントが受信されます。

192.168.1.42 のリモートホストにポート8888 で接続します。割り当てられた connectionId が返されます。


connectionId = NetworkTransport.Connect(hostId, "192.168.1.42", 8888, 0, out error);

接続が完了したら、ConnectEvent を受信します。これで、データの送信を開始できます。


NetworkTransport.Send(hostId, connectionId, myReliableChannelId, buffer, bufferLength,  out error);

接続を終了したいときは、 Disconnect() を呼び出してホストの接続を切ります。

NetworkTransport.Disconnect(hostId, connectionId, out error);

関数の呼び出しが成功したかを確認するには、out errorNetworkError にキャストします。NetworkEror.Ok はエラーがなかったことを示します。

ホストの状態を確認するには、以下の 2 つの関数を使用できます。

内部イベントキューからイベントをポーリングする場合は、以下のうち、いずれかを呼び出します。

NetworkTransport.Receive(out recHostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);

または、 NetworkTransport.ReceiveFromHost(recHostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);

これらの関数は両方とも、キューからイベントを返します。最初の関数は任意のホストからイベントを返し、recHostId 変数にはメッセージ元のホストID が割り当てられます。2 番目の関数は、指定した recHostId によって特定されるホストからのイベントのみを返します。

Receive からデータをポーリングする方法の 1 つは、 Update関数内でそれらを呼び出すことです。


void Update()
{

    int recHostId; 
    int connectionId; 
    int channelId; 
    byte[] recBuffer = new byte[1024]; 
    int bufferSize = 1024;
    int dataSize;
    byte error;
    NetworkEventType recData = NetworkTransport.Receive(out recHostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);
    switch (recData)
    {
        case NetworkEventType.Nothing:                     break;
        case NetworkEventType.ConnectEvent:                break;
        case NetworkEventType.DataEvent:                   break;
        case NetworkEventType.DisconnectEvent:            break;

  case NetworkEventType.BroadcastEvent:

       break;
    }
}

取得できるイベントには 5 種類あります。


case NetworkEventType.ConnectEvent: 
    if(myConnectionId == connectionId)
        //接続リクエストが承認されました
    else
        //だれかから接続リクエストを受信しました
    break;

  • NetworkEventType.DataEvent: データイベントを受信しました。データイベントは、受け取るべきデータがある場合に受信します。recBuffer がデータを格納するのに十分な大きさであれば、データはバッファーにコピーされます。 そうでない場合、イベントには MessageToLong ネットワークエラーが含まれます。 このような場合は、バッファーをより大きなサイズに再割り当てし、DataEvent 関数を再度呼び出す必要があります。

  • NetworkEventType.DisconnectEvent: 既存の接続が切断されたか、接続リクエストが失敗しました。 発生原因を見つけるにはエラーコードを確認してください。


case NetworkEventType. DisconnectEvent: 
    if(myConnectionId == connectionId)
        //何らかの理由で接続できません。エラーを見てください
    else
        //既存の接続の 1 つが切断されました
    break;

WebGL サポート

WebGL では WebSocket を使用できます。ただし、ウェブクライアントはそれ自体がホストになれないため、ホストにしか接続できません。つまり、ホストはスタンドアロンのプレイヤー (Win、Mac、Linux のみ) でなければなりません。 クライアント側の設定は、前出で説明されてたすべてのステップ (トポロジーと設定を含む) は同じです。 サーバー側で、以下を呼び出します。


NetworkTransport.AddWebsocketHost(topology, port, ip);

上の IP アドレスには、リッスンすべき特定のアドレスを渡します。または、ホストにすべてのネットワークインターフェースをリッスンさせたい場合は、 IP アドレスに null を渡します。

サーバーが 1 度にサポートできる WebSocket ホストは 1 つだけですが、サーバーは他のジェネリック なホストを同時に処理できます。


NetworkTransport.AddWebsocketHost(topology, 8887, null);
NetworkTransport.AddHost(topology, 8888);

Multiplayer のロビー
NetworkReader と NetworkWriter シリアライザー