読者です 読者をやめる 読者になる 読者になる

tanaka's Programming Memo

プログラミングについてのメモ。

Unity5.1 ネットワークシステム PlayerObjects

前へ | 次へ

UNet Unity5.1からの新しいネットワークシステム
Unity5.1のネットワークマニュアル斜め読み(2)
UNet NetworkManagerの利用
Unity5.1 ネットワークシステムのオブジェクト生成
Unity5.1 ネットワークシステムの状態同期
Unity5.1 ネットワークシステムのRemote Actions
Unity5.1 ネットワークシステム PlayerObjects
Unity5.1 ネットワークシステム Object Visibility
Unity5.1 ネットワークシステム Network Messages
Unity5.1 ネットワークシステム Matchmaker
Unity5.1 ネットワークシステム Scene Objects
Unity5.1 ネットワークシステム:シングルプレイヤーゲームを多人数に対応させる
Unity5.1 ネットワークシステム Multiplayer Lobby
Unity5.1 ネットワークシステム Network Clients and Servers
Unity5.1 ネットワークリファレンス概要

公式サイトの以下のページの斜め読みメモです。
Unity - マニュアル: Player Objects

PlayerObjects

ネットワークシステムのHLAPIでは、プレイヤーは特殊なオブジェクトです。プレイヤーはサーバー上にあるので、プレイヤーのクライアントからCommands(安全にクライアントからサーバーに送られるRPC)で指示が実行されます。このサーバーが管理するシステムでは、プレイヤーではないその他のサーバー上のオブジェクトは、クライアント上のオブジェクトからのコマンドを直接受信することができません。これはセキュリティー上の問題と、分散した環境で挙動が複雑化するのを防ぐ意味があります。すべてのユーザーからのCommandsは、プレイヤーを操作するクライアントのプレイヤーオブジェクトから送信することで、正しい現在位置の中心座標を使うことができます。

初期動作では、NetworkManagerを使うとサーバーにクライアントが接続した時にプレイヤーが追加されます。しかし、なんらかの入力が行われるまでプレイヤーを出現させたくない場合もあるでしょう。その際には、NetworkManagerのAutoCreatePlayer欄のチェックを外します。プレイヤーが追加されると、NetworkManagerはプレイヤープレハブからオブジェクトを生成し、接続に関連付けます。これはNetworkManagerによってNetworkServer.AddPlayerForConnection()が呼ばれることで実現します。この振る舞いはNetworkManager.OnServerAddPlayer()をオーバーライドすることで変更できます。OnServerAddPlayer()の初期動作では、新しいプレイヤーのインスタンスをプレイヤープレハブから生成してから、NetworkServer.AddPlayer()を使って新しいプレイヤーインスタンスをspawnします。OnServerAddPlayer()をカスタマイズ実装する時は、NetworkServer.AddPlayer()を必ず呼ぶ必要があります。それ以外の初期化の振る舞いに制限はありません。プレイヤーの色をカスタマイズする例を以下に挙げます。公式サイトからの抜粋です。

class Player : NetworkBehaviour
{
    [SyncVar]
    public Color color;
}

class MyManager : NetworkManager
{
    public override void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
    {
        GameObject player = (GameObject)Instantiate(playerPrefab, Vector3.Zero, Quaternion.Identity);
        player.GetComponent<Player>().color = Color.Red;
        NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);
    }
}


NetworkServer.AddPlayer()関数は、OnServerAddPlayer()関数内で呼ばれる必要はありません。playerControlledIdが渡されているオブジェクトが接続されている間であれば、OnServerAddPlayer()の処理後にAddPlayer()を呼び出しても構いません。これによりリモートのデータソースからプレイヤーデータを読み込むような(時間がかかる可能性のある)処理を非同期に実行できます。

HLAPIはプレイヤーとクライアントを別のオブジェクトとして扱います。多くのケースではクライアントごとに1つのプレイヤーオブジェクトを持ちます。しかし、コンソールに複数のコントローラーが繋がっているような場合は、1つの接続で複数のプレイヤーを操作することができます。1つの接続に複数のプレイヤーがいる場合、playerControllerIdプロパティーで個別に識別します。これは接続に対する識別子になります。クライアント上のプレイヤーを管理するコントローラーのIDに割り当てられます。

プレイヤーオブジェクトはサーバー上でNetworkServer.AddPlayerForConnection()に渡されて、システムによって自動的にspawnされます。そのため、プレイヤーのためにNetworkServer.Spawn()を呼び出す必要はありません。プレイヤーの準備が整ったら、シーン内の有効なNetworkIdentityオブジェクトがプレイヤーのクライアント上にspawnされます。それからゲーム内のすべてのネットワークオブジェクトが最新の状態でクライアント上に生成されて、ゲームのその他の参加者と同期されます。

NetworkManager上のプレイヤープレハブはプレイヤーオブジェクトを作成するためのみに利用する必要はありません。別のプレイヤーの生成のために別のメソッドを利用しても構いません。

AddPlayerForConnection()関数は、OnServerAddPlayer()内で必ずしも呼び出す必要はありません。どのようなプレイヤーを作成するかの情報をデータベースから取得するような場合に非同期に実行することができます。

Ready State

プレイヤーに加えて、クライアントの接続にも準備が完了したことを表す"ready"状態があります。準備ができたクライアントには、spawnされたオブジェクトが送られてきて、状態の同期が開始されます。準備が完了していないクライアントにはこれらは送られてきません。クライアントがサーバーに最初に接続した時はまだ準備が整っていません。この状態の間、クライアントが実行できるのはシーンを読み込んだり、アバターを選択したり、ログイン欄を入力したりするなど、サーバーのシミュレーションによるリアルタイムのインターラクションを必要としない処理のみです。クライアントがすべてのゲーム前の作業を完了して、必要なアセットが全て読み込まれると、ClientScene.Ready()が呼び出されて準備完了状態に移行します。簡単な例でも挙動が確認できますが、NetworkServer.AddPlayerForConnection()によって追加されたプレイヤーがまだ状態が設定されていなかったら、ready状態にクライアントを移行させます。

クライアントは準備完了を待たずにネットワークメッセージを送受信することができます。つまり、ネットワークメッセージの送受信にアクティブなプレイヤーは不要ということです。そのため、メニュー画面や選択画面中のクライアントがゲームに接続して、プレイヤーなしでゲームに作用を及ぼすことができます。これについては、このドキュメントのCommandsやRPCによる呼び出しなしでメッセージを送信する方法についての項目で述べます。

Switching Players

接続されたプレイヤーオブジェクトは、NetworkServer.ReplacePlayerForConnection()関数を使って置き換えることができます。これにより、ゲームを開始する前のロビー画面などでプレイヤーに関連するCommandsの実行時間を削減することができます。この関数はAddPlayerForConnection()と同じ引数を受け取りますが、接続のためにすでに作成済みのプレイヤーを存在させたままにできます。古いプレイヤーはDestroyする必要はありません。NetworkLobbyManagerは、全てのロビー内のプレイヤーの準備が整って、LobbyPlayerからゲームプレイをするプレイヤーに切り替える際にこのテクニックを利用しています。

これはまた、destroyされたオブジェクトからプレイヤーをrespawnするときにも利用できます。単にオブジェクトを無効にしてからゲームの属性をリセットしてrespawnすることもできますが、以下のコードのように新しいオブジェクトに置き換えることもできます。以下、公式サイトからの抜粋です。

class GameManager
{
    public void PlayerWasKilled(Player player)
    {
        var conn = oldPlayer.connectionToClient;
        var newPlayer = Instantiate<GameObject>(playerPrefab);
        Destroy(oldPlayer.gameObject);
    
        NetworkServer.ReplacePlayerForConnection(conn, newPlayer, 0);
    }
}

接続のためのプレイヤーオブジェクトがdestroyされると、そのクライアントではCommandsを実行できなくなります。ただし、ネットワークメッセージは送信できます。

ReplacePlayerForConnection()関数を使うには、プレイヤーのクライアントから、オブジェクトとクライアントを関連付けるためのNetworkConnectionオブジェクトが必要です。これは大抵NetworkBehaviourクラスのconnectionToClientプロパティーを利用します(例も同様)。しかし、古いプレイヤーがすでにdestroyされていた場合は利用できません。

接続を探すために利用できるリストがあります。NetworkLobbyManagerを利用している場合は、lobbySlotsにロビーにいるプレイヤーが列挙されます。また、NetworkServerconnectionslocalConnectionsのリストを持っています。




前へ | 次へ