UnityNetworkでオンライン対応9-使用するプレイヤーをロビーで変更する-

前回はロビーのアセットを使用してロビー機能を使えるようにしました。

UnityのアセットストアにあるNetwork Lobbyアセットを使ってロビー機能を自分のゲームに搭載してみます。

前回まではあらかじめ指定した1人のキャラクターを変更する事が出来ず、オンラインに接続したプレイヤーキャラクターは固定になっていました。

そこで、今回はロビーにいる時に操作するプレイヤーキャラクターを変更出来るようにしたいと思います。

しかし、ここで結構つまづきました。(^_^;)

YouTubeにプレイヤーキャラクターを変更するやり方を載せている方がおられましたので、そちらを大いに参考にさせて頂きました。

https://www.youtube.com/watch?v=lol2uDGV-_E(動画が消えていましたので動画のリンク先だけ残しておきます)

ありがたやありがたや(ノ´▽`)ノ{+++THANK YOU+++}ヽ(´▽`ヽ)

実際に作ったサンプルは

↑のようになりました。

前回使ったNetwork Lobbyアセットのプレハブやスクリプトを変更していきます。

スポンサーリンク

プレイヤーキャラクターを変更する機能

他のプレイヤーキャラクターとボタン用アイコンの作成

プレイヤーキャラクターを変更する機能を搭載するに前に、操作出来るプレイヤーキャラクターをもう一人作成しておきます。

まずはプレイヤーキャラクターであるUnityNetworkPlayerプレハブをCtrl+DキーでコピーしRedEthanという名前に変更します。

次にAssetsフォルダ内で右クリック→Create→Materialを選択し、新しいマテリアルを作成し、名前をRedPlayerとします。

RedEthanのAlbedoの右のカラーボックス部分をクリックし、色を赤色にします。

RedEthan用の赤いマテリアルを作成

次に先ほどコピーして作ったRedPlayerのモデルの体とサングラス部分にRedEthanマテリアルをドラッグ&ドロップします。

RedEthanのモデルに赤いマテリアルを設定

↓がRedPlayerの体モデルにRedEthanマテリアルを設定したものです。

RedEthanの体モデルに赤いマテリアルを設定

次にボタンに表示するアイコンを作成します。

UnityNetworkPlayerとRedPlayerをシーン上に置き、Snipping Tool等を使ってキャラクターの顔部分を切り取り、パソコンにPNG形式等で保存します。

Snipping ToolでEthanの顔部分を切り取って保存

パソコンにファイルを保存したら、そのファイルをUnityのAssetsフォルダにドラッグ&ドロップするか、右クリック→Import New Asset…を選択し、ファイルを取り込みます。

ボタン用アイコン画像の取り込み

↑のように画像を取り込んだら、白いEthan用アイコンはWhiteEthan、赤いEthan用アイコンはRedEthanとし、インスペクタでTexture TypeをSpriteに変更します。

TextureTypeをSpriteにする

これで他のプレイヤーキャラクターとボタン用アイコンの作成が出来ました。

PlayerInfoの改造

キャラクター切り替えボタンをPlayerInfoに取り付けなければいけないので、PlayerInfoを改造します。

次にLobbyManagerPrefabに該当するLobby→Prefabs→PlayerInfoをLobbyManagerのPlayerListの子要素にドラッグ&ドロップします。

PlayerListの子要素にPlayerInfoを配置する

PlayerInfoはロビーに接続したプレイヤーの情報を保持するプレハブなので、プレイヤー毎にPlayerListの子要素に作られます。

↑の画像ではすでに作られていますが、PlayerInfoの子要素にUIのButtonを作成し、名前をChangeCharaButtonとします。

ChangeCharaButtonの子要素のTextは使わないので削除してかまいません。

ChangeCharaButtonのImage SourceにWhiteEthanを設定します。

キャラクター切り替えボタンのサイズやアイコンの変更

ボタンのサイズ変更をし調整します。

また、ボタンは最初は有効にしたくない為、ButtonのInteractableのチェックを外しておきます。

PlayerInfoの部分を見ると、

キャラクター切り替えボタンが表示された

↑のようにキャラクターイメージが表示されたボタンが作成されます。

ここまで出来たらシーン上のPlayerInfoを削除します。

キャラクター変換ボタンを押した時の処理

ロビーに入ったらPlayerInfoがプレイヤー数分作成されますが、キャラクター変更ボタンを押した時にボタンのアイコンの変更や他のプレイヤー用のボタンを操作出来ないようにし、自分のプレイヤー用のボタンは操作出来るようにしなければいけません。

そこで、PlayerInfoプレハブに取り付けてあるLobbyPlayerスクリプトの中身に処理を追加していきます。

まずはフィールド部分です。

キャラクター切り替えボタン、ボタン用のアイコンの種類、ボタンのアイコンに今何が設定されているかの情報をフィールドとして持たせます。

buttonImageStringはSyncVarアトリビュートを取り付け、値の変化があった時に値を同期出来るようにします。

またフックを用いて値が変わった時にOnChangeButtonImageメソッドを呼び出します。

次にOnClientEnterLobbyメソッドの最後に処理を追加します。

OnClientEnterLobbyメソッドはクライアントがロビーに入った時に呼び出されるので、そこでボタンのアイコンを変更します。

次にSetupLocalPlayerメソッドの最後の方のreadyButtonにイベントリスナーを登録した後に4行追加します。

SetupLocalPlayerメソッドはクライアントがロビーに入った時でローカルプレイヤーだった時に実行されます。

ここではキャラクター切り替えボタンの有効化、ボタンが押された時のイベントリスナーにOnChangeCharaButtonを登録し、CmdOnChangeButtonImageメソッドを呼び出してサーバー上でボタンのアイコンを同期させる処理を記述しています。

OnChangeCharaButtonメソッドはボタンを押された時に実行する自前のメソッドです。

indexはLobbyManagerコンポーネントのRegisterd Spawnable Prefabsに設定した順番と合わせます。

ボタンのアイコンがwhiteEthanSpriteと同じであればredEthanSpriteに切り替えindexを1、それ以外の時はwhiteEthanSpriteにし、indexを0にしています。

CmdChangeCharaはプレイヤーキャラクターを変更した事をサーバーで登録する自前のメソッドです。

CmdOnChangeButtonImageはボタンのアイコンを変更する処理をサーバーで実行する自前のメソッドです。

CmdChangeCharaメソッドではLobbyManagerスクリプトの自前のメソッドであるSetPlayerを呼び出しています。

これはクライアントの情報とボタンのアイコンのindex値を渡してLobbyManagerでデータを保持させます。

OnChangeButtonImageとCmdOnChangebuttonImageはほぼ同じ事をしていますが、OnChangebuttonImageはbuttonImageStringが変更された時にフックで呼び出されるメソッドで、CmdOnChangebuttonImageはサーバーで実行されるメソッドです。

簡略化出来そうですね・・・。

LobbyManagerスクリプトに処理を追加

LobbyPlayerスクリプトからLobbyManagerスクリプトにアクセスし、プレイヤー毎のキャラクターを管理するようにする必要があります。

まずはLobbyManagerスクリプトにフィールドを追加します。

このcurrentPlayersディクショナリはサーバーに接続しているプレイヤーのコネクションIDとRegistered Spawnable Prefabsの登録順を保存するフィールドになります。

つまり、接続したクライアントに対応するプレイヤーキャラクターを保持するものです。

次にOnLobbyServerCreateLobbyPlayerの最初に処理を追記します。

OnLobbyServerCreateLobbyPlayerメソッドはロビーに入った時にロビープレイヤー(PlayerInfo)を作成した時にサーバーで呼ばれます。

ここで接続してきた相手のコネクションIDがcurrentPlayersに登録されていなければ、コネクションIDをキーにし、値に0を入れています。

0はRegistered Spawnable Prefabsの0番目のプレハブを設定します(今回はUnityNetworkPlayerを設定予定)。

つまりプレイヤーの初期キャラクター情報を保持しています。

次にSetPlayerメソッドを記述します。

SetPlayerは先ほど記述したLobbyPlayerスクリプトのCmdChangeCharaメソッドから呼び出していました。

currentPlayersにコネクションIDが登録されていれば、ディクショナリにコネクションIDをキーにしてキャラクターのindex値を値として入れています。

次にOnLobbyServerCreateGamePlayerメソッドをオーバーライドして作成します。

OnLobbyServerCreateGamePlayerメソッドはサーバーで呼び出され、ゲームシーンに移動する時に呼び出されプレイヤーキャラクターを作成します。

ここではcurrentPlayersからコネクションIDに対応するキャラクターの位置であるindexを取得し、そのインデックス値を元にspawnPrefabs配列からプレイヤーキャラクターを取得し、インスタンス化しています。

そのインスタンス化したプレイヤーキャラクターを戻り値とする事で、それぞれのキャラクターが対応するキャラクターに変更されます。

これで機能の作成が出来ましたのでインスペクタで設定をします。

PlayerInfoのLobbyPlayerではPlayerInfoの子要素のChangeCharaButtonや作成したボタン用アイコンを設定します。

LobbyPlayerのインスペクタの設定

LobbyManagerではRegistered Spawnable Prefabsの0番目にUnitynetworkPlayer、1番目にRedPlayerが来るように設定します。

LobbyManagerのインスペクタの設定

これで設定が出来ましたので、確認してみてください。

終わりに

操作キャラクターの変更方法が全然わからなくて大変でした。(^_^;)

動画主のおかげです。(._.)

ゲーム中のプレイヤーキャラクターの切り替えに関してはNetworkServer.ReplacePlayerForConnectionを使うと出来そうですね。

Unity のマルチプレイヤー HLAPI システムは、プレイヤーゲームオブジェクトをノンプレイヤーゲームオブジェクトと区別して扱います。新しいプレイヤーがゲームに参加すると (新しいクライアントがサーバーに接続すると)、そのプレイヤーのゲームオブジェクトはそのプレイヤーのクライアント上の「ローカルプレイヤー」ゲームオ...

一からネットワーク接続処理を作っていけば細かい対応も出来そうですが、今のところわたくしには難しいです。(ーー;)

スポンサーリンク

記事をシェアして頂ける方はこちら

フォローして頂くとやる気が出ます

コメント

  1. しいたけ より:

    素早い対応ありがとうございます。
    エラーは
    NullReferenceException: Object reference not set to an instance of an object
    Prototype.NetworkLobby.LobbyManager.OnLobbyServerCreateLobbyPlayer (UnityEngine.Networking.NetworkConnection conn, Int16 playerControllerId) (at Assets/Lobby/Scripts/Lobby/LobbyManager.cs:285)
    UnityEngine.Networking.NetworkLobbyManager.OnServerAddPlayer (UnityEngine.Networking.NetworkConnection conn, Int16 playerControllerId) (at C:/buildslave/unity/build/Extensions/Networking/Runtime/NetworkLobbyManager.cs:351)
    UnityEngine.Networking.NetworkManager.OnServerAddPlayerMessageInternal (UnityEngine.Networking.NetworkMessage netMsg) (at C:/buildslave/unity/build/Extensions/Networking/Runtime/NetworkManager.cs:873)
    UnityEngine.Networking.NetworkConnection.InvokeHandler (Int16 msgType, UnityEngine.Networking.NetworkReader reader, Int32 channelId) (at C:/buildslave/unity/build/Extensions/Networking/Runtime/NetworkConnection.cs:220)
    UnityEngine.Networking.NetworkServer.InvokeHandlerOnServer (UnityEngine.Networking.ULocalConnectionToServer conn, Int16 msgType, UnityEngine.Networking.MessageBase msg, Int32 channelId) (at C:/buildslave/unity/build/Extensions/Networking/Runtime/NetworkServer.cs:1654)
    UnityEngine.Networking.ULocalConnectionToServer.Send (Int16 msgType, UnityEngine.Networking.MessageBase msg) (at C:/buildslave/unity/build/Extensions/Networking/Runtime/LocalConnections.cs:83)
    UnityEngine.Networking.ClientScene.AddPlayer (UnityEngine.Networking.NetworkConnection readyConn, Int16 playerControllerId, UnityEngine.Networking.MessageBase extraMessage) (at C:/buildslave/unity/build/Extensions/Networking/Runtime/ClientScene.cs:200)
    UnityEngine.Networking.ClientScene.AddPlayer (UnityEngine.Networking.NetworkConnection readyConn, Int16 playerControllerId) (at C:/buildslave/unity/build/Extensions/Networking/Runtime/ClientScene.cs:133)
    UnityEngine.Networking.ClientScene.AddPlayer (Int16 playerControllerId) (at C:/buildslave/unity/build/Extensions/Networking/Runtime/ClientScene.cs:127)
    UnityEngine.Networking.NetworkManager.OnClientConnect (UnityEngine.Networking.NetworkConnection conn) (at C:/buildslave/unity/build/Extensions/Networking/Runtime/NetworkManager.cs:1118)
    UnityEngine.Networking.NetworkLobbyManager.OnClientConnect (UnityEngine.Networking.NetworkConnection conn) (at C:/buildslave/unity/build/Extensions/Networking/Runtime/NetworkLobbyManager.cs:557)
    Prototype.NetworkLobby.LobbyManager.OnClientConnect (UnityEngine.Networking.NetworkConnection conn) (at Assets/Lobby/Scripts/Lobby/LobbyManager.cs:401)
    UnityEngine.Networking.NetworkManager.OnClientConnectInternal (UnityEngine.Networking.NetworkMessage netMsg) (at C:/buildslave/unity/build/Extensions/Networking/Runtime/NetworkManager.cs:923)
    UnityEngine.Networking.NetworkConnection.InvokeHandler (UnityEngine.Networking.NetworkMessage netMsg) (at C:/buildslave/unity/build/Extensions/Networking/Runtime/NetworkConnection.cs:231)
    UnityEngine.Networking.LocalClient.ProcessInternalMessages () (at C:/buildslave/unity/build/Extensions/Networking/Runtime/LocalClient.cs:141)
    UnityEngine.Networking.LocalClient.Update () (at C:/buildslave/unity/build/Extensions/Networking/Runtime/LocalClient.cs:69)
    UnityEngine.Networking.NetworkClient.UpdateClients () (at C:/buildslave/unity/build/Extensions/Networking/Runtime/NetworkClient.cs:947)
    UnityEngine.Networking.NetworkIdentity.UNetStaticUpdate () (at C:/buildslave/unity/build/Extensions/Networking/Runtime/NetworkIdentity.cs:1091)
    となっています。長くてすいません…

    • うーむ・・・エラーが多すぎてわかりませんが・・・。

      とりあえず1行目でNullReferenceException: Object reference not set to an instance of an objectと出てるので

      currentPlayersのインスタンスが生成されていない可能性があります。

      Startメソッド等で

      ↑のようにcurrentPlayersのインスタンスを生成してみてください。

      • しいたけ より:

        ありがとうございます。これでロビーに入り、キャラを選択するまではできました。
        ですが、joinを押してもキャラが作成されないです。
        エラーは
        ArgumentOutOfRangeException: Argument is out of range.
        Parameter name: index
        と出ています。

        • ArgumentOutOfRangeExceptionなので範囲外を指定してエラーになっているということで、その際のパラメータがindexという事なので、

          LobbyManagerのインスペクタのRegistered Spawnable Prefabsにプレイヤーキャラクターのプレハブを設定していないのかもしれません。

          ここで設定する順番とキャラクター番号が一致するように並びを変える必要があります。

          • しいたけ より:

            一様Registered Spawnable Prefabsにはプレイヤーキャラクターのプレハブは設定されていたんですけど、Debug.Logで確認したところ、joinを押してPlay Sceneに移動するときでも、しっかりとindexの値を認識してくれていたので、問題はプレイヤーキャラクターのプレハブにあるんですか、それとも、私のプレイヤーキャラクターを作成するスクリプトに抜けているところがあったんでしょうか

          • どのメソッドのindex変数のエラーが出ていますか?

            範囲外エラーなのでキャラクターを生成する時のindexがプレイヤーキャラのリストの範囲外を指定していることで出ているエラーだとは思うんですが。

            この機能はUnity5.3.4f1で作成しており、Unity5.3.4f1では問題なく動作しているんですが、Unity2017では確認していないのでバージョン違いで動作しないという可能性もあります。

          • しいたけ より:

            OnLobbyServerCreateGamePlayerのメソッドのindexがエラーを起こしているみたいです。私はunity 5.6.1f1を使っていたのでUnity5.3.4f1でも一度試してみます。

          • 原因がわかりました。

            おそらくOnLobbyServerCreateGamePlayerメソッドでプレイヤーキャラクターをインスタンス化していますが、その時にキャラクターの登場位置をNetworkStartPositionコンポーネントの位置からランダムな位置を取得し配置させていますが、

            LobbyManagerのPlay Sceneに設定したシーンでNetworkStartPositionコンポーネントを取り付けたゲームオブジェクトがないとエラーになると思われます。

            https://gametukurikata.com/online/unitynetwork/unitynetwork5

            の記事を参照して頂きNetworkStartPositionを設定するか、

            OnLobbyServerCreateGamePlayerメソッドでNetworkStartPositionに設定した位置を使用せず、例えば(0, 0, 0)の位置にキャラクターを登場させる場合はGameObject.Instantiateの第2引数でVector3.zeroを指定します。

            おそらくこれでキャラクターの切り替えは可能だと思います。

            Unity2017で確認したところ問題ありませんでした。

          • しいたけ より:

            出来ました!本当に素早い対応ありがとうございました。

  2. しいたけ より:

    一様このサイト通りにプログラムを追加していったつもりなんですけど、
    public override GameObject OnLobbyServerCreateLobbyPlayer(NetworkConnection conn, short playerControllerId)
    の中にある
    if (!currentPlayers.ContainsKey (conn.connectionId)) {
    currentPlayers.Add (conn.connectionId, 0);
    }
    にエラーがかかってしまいます。この部分をオフにするとキャラ選択まではできるのですが、joinを押してもキャラが作成されません。どこに問題点があるのでしょうか?

    • こんにちは(^^)/

      ちなみにどんなエラー内容が出ていますか?

      エラーの内容がわかれば対処できる可能性もあります。

      ネットワーク関連はなかなか返答が難しいですが・・・・(^_^;)