マルチプレイヤープロジェクトの設定
Network Manager HUD の使用

NetworkManager の使用

Network Manager はマルチプレイヤー ゲームのネットワークを管理するためのコンポーネントです。

NetworkManager の機能には下記が含まれます。

  • ゲームの状態の管理

  • オブジェクト生成 (Spawning) の管理

  • シーンの管理

  • デバッグ情報

  • マッチメイキング

  • カスタマイズ

Network Manager を使用する前に

Network Manager はマルチプレイヤーゲームの核となる管理コンポーネントです。まず、最初のシーンに空のゲームオブジェクトを作成し、 NetworkManager コンポーネントを加えます。新しく加えた NetworkManager コンポーネントは以下のように表示されます。

インスペクター ウィンドウに表示された Network Manager
インスペクター ウィンドウに表示された Network Manager

エディターの Network Manager のインスペクターによって、ネットワークに関連するさまざまな事項の設定と制御を行えます。

注意: 各シーンにはアクティブな Network Manager を 1 つだけ設定してください。すでに通信状態のゲームオブジェクト (Network Identity コンポーネントを持つオブジェクト) に Network Manager を設定しないようにしてください。なぜなら、シーンをロードするときに Unity はこれらを無効にしてしますからです。

マルチプレイヤーゲームの開発に精通しているユーザーは、Network Manager コンポーネントがすべて 高レベル API (HLAPI) を使用して実装されていることを知っていると便利です。そのため、Network Manager コンポーネントが行うことはすべて、スクリプトを通して行うこともできます。精通したユーザーの場合は、Network Manager コンポーネントの機能を拡張する必要がある場合は、スクリプトを使用して NetworkManager から独自のクラスを派生させ、仮想関数のフックをオーバーライドしてその挙動をカスタマイズできます。ただし、Network Manager コンポーネントは、多くの便利な機能を 1 カ所にまとめ、マルチプレイヤーゲームの作成、実行、デバッグを可能な限りシンプルにできます。

ゲームの状態の管理

Unet のマルチプレイヤーゲームは 3 つのモードで実行できます - クライアント、専用サーバー、あるいは、クライアントであると同時にサーバーでもある「ホスト」です。

Network Manager HUD を使用する場合は、プレイヤーの選択に基づいて Network Manager HUD が自動的に Network Manager にどのモードで開始するかを知らせます。プレイヤーがゲームを開始するための独自の UI を作成する場合は、これは独自のコードで呼び出す必要があります。以下のメソッドを使用できます。

Network Manager コンポーネントのネットワークアドレスとポート設定
Network Manager コンポーネントのネットワークアドレスとポート設定

クライアント、サーバー、ホストのどのモードでゲームが開始したとしても、Network AddressNetwork Port プロパティーを使用します。クライアントモードでは、ゲームは指定されたアドレスとポートに接続しようとします。サーバー、または、ホストモードでは、指定したポートに入ってくる接続をリッスンします。

ゲームの開発中に、これらのプロパティーに固定アドレスとポートを設定すると便利です。ただし、最終的には、プレイヤーが接続するホストを選択できるようにしたい場合があります。そのような場合は、Network Discovery コンポーネント (Local Discovery 参照) を使用して、ローカルエリアネットワーク (LAN) でアドレスとポートをブロードキャストして見つけられるようにし、Matchmaker サービスを使用して、プレイヤーがインターネットのマッチを見つけて Multiplayer Service に接続できるようにします。

オブジェクト生成 (Spawning) の管理

通信状態のゲームオブジェクトの生成 (ネットワーク化されたインスタンシエーション) を管理するには Network Manager を使用します。

Network Manager コンポーネントの Spawn Info セクション
Network Manager コンポーネントの Spawn Info セクション

ほとんどのゲームにはプレイヤーを表すプレハブがあるため、Network Manager に Player Prefab フィールドがあります。このフィールドにはプレイヤーのプレハブ を割り当てる必要があります。Player Prefab を設定すると、プレイヤーのゲームオブジェクトはゲームの各ユーザーのプレハブから自動的に生成されます。これは、ホストされるサーバーのローカルプレーヤーと、リモートクライアントのリモートプレイヤーに適用されます。プレイヤープレハブには、Network Identity コンポーネントをアタッチする必要があります。

プレイヤープレハブを割り当てると、ホストとしてゲームを開始することができ、プレイヤーゲームオブジェクトが生成されます。ゲームを停止すると、プレイヤーゲームオブジェクトは破棄されます。ゲームの別のコピーを作成して実行し、それを ローカルホスト にクライアントとして接続すると、Network Manager は別のプレイヤーゲームオブジェクトを生成します。そのクライアントを停止すると、そのプレイヤーのゲームオブジェクトは破棄されます。

プレイヤープレハブに加えて、ゲーム中に動的に生成したい他のプレハブも Network Manager で加える必要があります。

インスペクター内の Registered Spawnable Prefabs という名のリストにプレハブを加えることができます。また、ClientScene.RegisterPrefab() メソッドを使用して、コードを使ってプレハブを加えることもできます。

Network Manager が 1 つしかない場合は、すべてのシーンで生成される可能性があるすべてのプレハブをそれに登録する必要があります。各シーンに別々の Network Manager がある場合は、そのシーンに関連するプレハブだけを加えます。

プレイヤーのインスタンス化のカスタマイズ

Network Manager は、NetworkManager.OnServerAddPlayer() を実装することによって、プレイヤーゲームオブジェクトを生成します。プレイヤーゲームオブジェクトの作成方法をカスタマイズしたい場合は、その仮想関数を上書きすることができます。 このコードは、デフォルト実装の例を示しています。

public virtual void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
{
    var player = (GameObject)GameObject.Instantiate(playerPrefab, playerSpawnPos, Quaternion.identity);
    NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);
}

**ノート: ** OnServerAddPlayer のカスタムバージョンを実装する場合、プレイヤーゲームオブジェクトが新しく作成されたときには NetworkServer.AddPlayerForConnection() メソッドを必ず呼び出す必要があります。それにより、ゲームオブジェクトが生成されてクライアントの接続と関連付けられるようになります。AddPlayerForConnection はゲームオブジェクトを生成するため、NetworkServer.Spawn() を使用する必要はありません。

開始位置

プレイヤーが生成される位置を制御するには、 Network Start Position コンポーネントを使用します。これらを使用するには、シーンのゲームオブジェクトに Network Start Position コンポーネントをアタッチし、プレイヤーを開始する位置にゲームオブジェクトを配置します。シーンには、好きなだけ多くの開始位置を追加できます。Network Manager はシーン内のすべての開始位置を検出し、各プレイヤーのインスタンスを生成するときに、そのうちの 1 つの位置と方向を使用します。

Network Manager には、Player Spawn Method プロパティーがあり、開始位置の選択方法を設定できます。

Random を選択すると、ランダムに選択された startPosition のオプションでプレイヤーを生成します。

Round Robin を選択すると、設定リストの startPosition のオプションを順番に繰り返します。

Random や Round Robin モードがゲームに適切でない場合は、コードを使って開始位置の選択方法をカスタマイズできます。 NetworkManager.startPositions リストで使用可能な Network Start Position コンポーネントにアクセスします。それから Network Manager のヘルパーメソッド GetStartPosition() を使用します。このヘルパーメソッドは開始位置を見つけるために OnServerAddPlayer の実装で使用されます。

シーンの管理

ほとんどのゲームは、複数のシーンを有しています。通常は少なくとも、ゲームを実際に再生するシーンの他にタイトル画面あるいはスタートメニューシーンがあります。NetworkManager は、シーンの状態とシーンの遷移を自動的に管理できるよう、マルチプレイヤーゲーム向けに設計されています。

NetworkManager インスペクターには 2 つのフィールドがあります。offlineScene と onlineScene です。これらのフィールドにシーンアセットをドラッグすると、ネットワーク化したシーン管理をアクティベートできます。

サーバーまたはホストを開始すると、オンラインシーンがロードされます。これが「現在の」ネットワークシーンになります。このサーバーに接続されたすべてのクライアントもまたシーンをロードするよう命令されます。このシーンの名前は networkSceneName プロパティーに保存されます。

サーバーまたはホストを停止するか、クライアントの接続が断たれることによってネットワークが停止されると、オフラインシーンがロードされます。これによって、マルチプレイヤーゲームへの接続が切れたときに、自動的にゲームのメニューシーンに戻るようになります。

また、NetworkManager.ServerChangeScene() の呼び出しによって、ゲームがアクティブな時にシーンを変更することも可能です。これにより、その時点で接続されているすべてのクライアントのシーンも変更され、networkSceneName が更新されて新しいクライアントも新しいシーンをロードします。

ネットワーク化したシーン管理がアクティブになっているときは、NetworkManager.StartHost() や NetworkManager.StopClient() などのゲーム状態管理の関数の呼び出しはすべて、シーン変更を生じさせます。これはランタイム制御 UI にも当てはまります。したがって、シーンを設定してこれらの関数を呼び出すことで、マルチプレイヤーゲームの流れを制御することができます。

シーン変更は、以前のシーンのすべてのゲームオブジェクトを破棄する原因になるので注意してください。

通常、NetworkManager がシーン間で存続するようにします。そうでないと、シーンが変わるたびにネットワーク接続が遮断されます。そのためには、インスペクターの Don’t Destroy On Load ボックスに必ずチェックを入れます。ただし、各シーンに設定の違う別々の NetworkManager を置くことも可能です。これは、繰り返しのプレハブの読み込みや異なるシーン遷移を制御したい場合に便利です。

カスタム化

NetworkManager クラスには、NetworkManager を継承する独自の派生クラスを作ることによってカスタマイズできる仮想関数があります。これらを、ます。これらの関数を実装する場合は、デフォルトによって提供されている機能をうまく処理するようにしてください。例えば、OnServerAddPlayer() で、接続のためにプレイヤーオブジェクトをアクティベートするためには関数 NetworkServer.AddPlayer を呼び出す必要があります。

これらはすべてホスト/サーバーとクライアントに発生するコールバックです。デフォルトの挙動を維持するために、基底クラスの関数を呼び出すことが重要な場合もあります。実装自体に関しては、networking bitbucket repository を参照してください。

using UnityEngine;

using UnityEngine.Networking;

using UnityEngine.Networking.Match;

public class CustomManager : NetworkManager {

    // サーバーコールバック

    public override void OnServerConnect(NetworkConnection conn) {

        Debug.Log("A client connected to the server: " + conn);

    }

    public override void OnServerDisconnect(NetworkConnection conn) {

        NetworkServer.DestroyPlayersForConnection(conn);

        if (conn.lastError != NetworkError.Ok) {

            if (LogFilter.logError) { Debug.LogError("ServerDisconnected due to error: " + conn.lastError); }

        }

        Debug.Log("A client disconnected from the server: " + conn);

    }

    public override void OnServerReady(NetworkConnection conn) {

        NetworkServer.SetClientReady(conn);

        Debug.Log("Client is set to the ready state (ready to receive state updates): " + conn);

    }

    public override void OnServerAddPlayer(NetworkConnection conn, short playerControllerId) {

        var player = (GameObject)GameObject.Instantiate(playerPrefab, Vector3.zero, Quaternion.identity);

        NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);

        Debug.Log("Client has requested to get his player added to the game");

    }

    public override void OnServerRemovePlayer(NetworkConnection conn, PlayerController player) {

        if (player.gameObject != null)

            NetworkServer.Destroy(player.gameObject);

    }

    public override void OnServerError(NetworkConnection conn, int errorCode) {

        Debug.Log("Server network error occurred: " + (NetworkError)errorCode);

    }

    public override void OnStartHost() {

        Debug.Log("Host has started");

    }

    public override void OnStartServer() {

        Debug.Log("Server has started");

    }

    public override void OnStopServer() {

        Debug.Log("Server has stopped");

    }

    public override void OnStopHost() {

        Debug.Log("Host has stopped");

    }

    //クライアントコールバック

    public override void OnClientConnect(NetworkConnection conn)

    {

        base.OnClientConnect(conn);

        Debug.Log("Connected successfully to server, now to set up other stuff for the client...");

    }

    public override void OnClientDisconnect(NetworkConnection conn) {

        StopClient();

        if (conn.lastError != NetworkError.Ok)

        {

            if (LogFilter.logError) { Debug.LogError("ClientDisconnected due to error: " + conn.lastError); }

        }

        Debug.Log("Client disconnected from server: " + conn);

    }

    public override void OnClientError(NetworkConnection conn, int errorCode) {

        Debug.Log("Client network error occurred: " + (NetworkError)errorCode);

    }

    public override void OnClientNotReady(NetworkConnection conn) {

        Debug.Log("Server has set client to be not-ready (stop getting state updates)");

    }

    public override void OnStartClient(NetworkClient client) {

        Debug.Log("Client has started");

    }

    public override void OnStopClient() {

        Debug.Log("Client has stopped");

    }

    public override void OnClientSceneChanged(NetworkConnection conn) {

        base.OnClientSceneChanged(conn);

        Debug.Log("Server triggered scene change and we've done the same, do any extra work here for the client...");

    }

}

NetworkManager のインスペクターでは、一部の接続のパラメーターと Timeout を変更することができます。パラメーターの中には、インスペクターで公開されていないものもありますが、コードを通して変更することができます。

using UnityEngine;

using UnityEngine.Networking;

public class CustomManager : NetworkManager {

    // カスタムの接続のパラメーターを早めに設定します。そうすると、遅すぎて使用されないという事態を防げます

    void Start()

    {

        customConfig = true;

        connectionConfig.MaxCombinedReliableMessageCount = 40;

        connectionConfig.MaxCombinedReliableMessageSize = 800;

        connectionConfig.MaxSentMessageQueueSize = 2048;

        connectionConfig.IsAcksLong = true;

        globalConfig.ThreadAwakeTimeout = 1;

    }

}
マルチプレイヤープロジェクトの設定
Network Manager HUD の使用