ホストマイグレーション
Unity Multiplayer の設定

Transport Layer API の使用

Unity の提供する高レベルのネットワーク API は、プレイヤーやネットワーク上の GameObject、その他ネットワークで必要となる機能を簡単に管理できるシステムを備えていますが、これに加えて “Transport Layer” と呼ばれる低レベル API も使用可能となっています。これにより、低レベルで自分のワーキングシステムを構築することが可能となっています。これは、より特殊な、あるいは高度な条件がゲームのネットワークに要求される場合に役に立つでしょう。

Transport Layer は、OS のソケットベースのネットワーキング上で機能する薄いレイヤーです。バイトの配列で表されるメッセージの送受信機能を備え、さまざまなケースに対応するために“quality of service(サービス品質)” を変更できるオプションを多数提供しています。Transport Layer は柔軟性とパフォーマンスに重点を置いたもので、UnityEngine.Networking.NetworkTransport クラス内の API で使用可能です。

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

  • 接続の確立
  • 異なる “quality of service(サービス品質)” による通信
  • フロー制御
  • 基数統計
  • リレーサーバー経由の通信やローカルディスカバリーなどの付加的サービス

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

  1. Network Transport Layer の初期化
  2. ネットワークトポロジーの設定
  3. ホストの作成
  4. 通信(接続およびメッセージの送受信)の開始
  5. 使用後はライブラリーを終了します。

Network Transport Layer の初期化

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

    // 引数無しで Transport Layer を初期化する (デフォルト設定)
        NetworkTransport.Init();
    // カスタム設定で Transport Layer を初期化する例
        GlobalConfig gConfig = new GlobalConfig();
        gConfig.MaxPacketSize = 500;
        NetworkTransport.Init(gConfig);

上記の二つ目の例では、Transport Layer は、値が 500 にカスタム設定された “MaxPacketSize” を以って初期化されています。カスタムの Init 値は、特殊なネットワーク環境を使用していて且つ必要な設定を明確に理解している場合にのみお使いください。基本的には、インターネットを介してプレイする典型的なマルチプレイヤーゲームの開発の場合、引数なしのデフォルト Init() 設定が適切です。

設定

次のステップは、ピア間の接続の設定です。異なる通信チャンネルをいくつか定義する必要があるかもしれません。各チャンネルのサービス品質レベルは、送りたい特定のメッセージのタイプや、ゲーム中におけるそれらの相対的な重要度に合うように設定します。

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

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

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

トポロジー

ネットワーク設定の最後のステップは、トポロジー定義です。ネットワーク トポロジーは、接続可能な回線の最大数および、使用される接続設定を定義します。

HostTopology topology = new HostTopology(config, 10);

ここで作成されたトポロジーでは、10 回線まで接続可能で、その各接続が、ひとつ前のステップで定義したパラメーターによって設定されるようになっています。

ホストの作成

すべての前準備が完了したら、ホスト(オープンソケット)を作成できます。

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

ここでは、ポート 8888 および任意の IP アドレスに新しいホストを追加します。このホストは最大10 件の接続に対応し、その各接続が config オブジェクトで定義したパラメーターを持ちます。

通信

ホストが作成されたら通信を開始できます。通信を行うには、各種コマンドをホストに送り、その状態を確認します。 送信できる主要なコマンドは3つあります。

connectionId = NetworkTransport.Connect(hostId, "192.16.7.21", 8888, 0, out error);
NetworkTransport.Disconnect(hostId, connectionId, out error);
NetworkTransport.Send(hostId, connectionId, myReiliableChannelId, buffer, bufferLength,  out error);
  1. 最初のコマンドは、IP が “192.16.7.21” でポートが 8888 のピアとして接続リクエストを送ります。この接続にアサインされた ID が返されます。
  2. 2つ目は、接続切断のリクエストを送ります。
  3. 3つ目は、connectionId と同等の ID を持つ接続へ、myReiliableChannelId と同等の ID を持った信頼できるチャンネルを使用して、メッセージを送ります。メッセージは buffer[] に保管され、メッセージの長さは bufferLength によって定義されます。

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

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);

これらは両方ともイベントを返しますが、一つ目の関数はどんなホストからでもイベントを返す(またホスト ID を recHostId を経由で返す)のに対し、二つ目は ID recHostId を持ったホストをチェックします。これらの関数はどれも 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:         //1
            break;
        case NetworkEventType.ConnectEvent:    //2
            break;
        case NetworkEventType.DataEvent:       //3
            break;
        case NetworkEventType.DisconnectEvent: //4
            break;
    }
}
  • ポイント 1: 特にイベントは何も返されていません。
  • ポイント 2: 接続イベントが届きました。これは新しい接続かもしれないし、前回の接続コマンドへの反応かもしれません。
myConnectionId = NetworkTransport.Connect(hostId, "192.16.7.21", 8888, 0, out error);
NetworkEventType recData = NetworkTransport.Receive(out recHostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);
switch (recData)
{
    case NetworkEventType.ConnectEvent: 
        if(myConnectionId == connectionId)
            //有効な接続リクエストが承認されました
        else
            //誰かが自分に接続しています
        break;
    //...   
}
  • ポイント 3: データが受領されました。この場合 recHostId がホストを定義し、connectionId が接続を定義し、channelId がチャンネルを定義します。dataSize は受領データのサイズを定義します。recBuffer がデータを持つに十分なサイズであれば、データがバッファーにコピーされます。そうでない場合は errorMessageToLong エラーが含まれることになり、バッファーを再割り当てしてこの関数を再度呼び出す必要があります。
  • ポイント 4: 接続切断信号が届きました。これは、確立された接続が切断されたという信号かもしれないし、接続リクエストが失敗したという信号かもしれません。
myConnectionId = NetworkTransport.Connect(hostId, "192.16.7.21", 8888, 0, out error);
NetworkEventType recData = NetworkTransport.Receive(out recHostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);
switch (recData)
{
    case NetworkEventType.ConnectEvent: 
        if(myConnectionId == connectionId)
            //有効な接続リクエストが承認されました
        else
            //誰かが自分に接続しています
        break;
    //...   
}

WebGL の対応

クライアント上において WebSocket が対応になりました。 クライアント側においても、上記のステップ(トポロジーおよびコンフィグを含む)はすべて同様であるはずです。ウェブクライアントは、スタンドアロンプレイヤーであるサーバーにのみ接続できます(Win、Mac、Linux のみ)。 サーバーでは以下を呼び出してください。

無効な IP または ポート

IP アドレスがリスニングアドレスである場合、IP アドレスに null を渡すことができます。ここではホストはすべてのネットワークインターフェースをリッスンします。サーバーが対応できる Websocket Host はひとつだけで、同時に一般的なホストを扱うこともできます。

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

これは ウェブソケットプロトコルを扱う tcp ソケットを 8887 ポートで開き、一般的なプロトコルを扱う udp ソケットは 8888 ポートを開きます。

ホストマイグレーション
Unity Multiplayer の設定