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

前回まではあらかじめ指定した1人のキャラクターを変更する事が出来ず、オンラインに接続したプレイヤーキャラクターは固定になっていました。
そこで、今回はロビーにいる時に操作するプレイヤーキャラクターを変更出来るようにしたいと思います。
しかし、ここで結構つまづきました。(^_^;)
YouTubeにプレイヤーキャラクターを変更するやり方を載せている方がおられましたので、そちらを大いに参考にさせて頂きました。
https://www.youtube.com/watch?v=lol2uDGV-_E(動画が消えていましたので動画のリンク先だけ残しておきます)
ありがたやありがたや(ノ´▽`)ノ{+++THANK YOU+++}ヽ(´▽`ヽ)
実際に作ったサンプルは
↑のようになりました。
前回使ったNetwork Lobbyアセットのプレハブやスクリプトを変更していきます。
プレイヤーキャラクターを変更する機能
他のプレイヤーキャラクターとボタン用アイコンの作成
プレイヤーキャラクターを変更する機能を搭載するに前に、操作出来るプレイヤーキャラクターをもう一人作成しておきます。
まずはプレイヤーキャラクターであるUnityNetworkPlayerプレハブをCtrl+DキーでコピーしRedEthanという名前に変更します。
次にAssetsフォルダ内で右クリック→Create→Materialを選択し、新しいマテリアルを作成し、名前をRedPlayerとします。
RedEthanのAlbedoの右のカラーボックス部分をクリックし、色を赤色にします。
次に先ほどコピーして作ったRedPlayerのモデルの体とサングラス部分にRedEthanマテリアルをドラッグ&ドロップします。
↓がRedPlayerの体モデルにRedEthanマテリアルを設定したものです。
次にボタンに表示するアイコンを作成します。
UnityNetworkPlayerとRedPlayerをシーン上に置き、Snipping Tool等を使ってキャラクターの顔部分を切り取り、パソコンにPNG形式等で保存します。
パソコンにファイルを保存したら、そのファイルをUnityのAssetsフォルダにドラッグ&ドロップするか、右クリック→Import New Asset…を選択し、ファイルを取り込みます。
↑のように画像を取り込んだら、白いEthan用アイコンはWhiteEthan、赤いEthan用アイコンはRedEthanとし、インスペクタでTexture TypeをSpriteに変更します。
これで他のプレイヤーキャラクターとボタン用アイコンの作成が出来ました。
PlayerInfoの改造
キャラクター切り替えボタンをPlayerInfoに取り付けなければいけないので、PlayerInfoを改造します。
次にLobbyManagerPrefabに該当するLobby→Prefabs→PlayerInfoをLobbyManagerのPlayerListの子要素にドラッグ&ドロップします。
PlayerInfoはロビーに接続したプレイヤーの情報を保持するプレハブなので、プレイヤー毎にPlayerListの子要素に作られます。
↑の画像ではすでに作られていますが、PlayerInfoの子要素にUIのButtonを作成し、名前をChangeCharaButtonとします。
ChangeCharaButtonの子要素のTextは使わないので削除してかまいません。
ChangeCharaButtonのImage SourceにWhiteEthanを設定します。
ボタンのサイズ変更をし調整します。
また、ボタンは最初は有効にしたくない為、ButtonのInteractableのチェックを外しておきます。
PlayerInfoの部分を見ると、
↑のようにキャラクターイメージが表示されたボタンが作成されます。
ここまで出来たらシーン上のPlayerInfoを削除します。
キャラクター変換ボタンを押した時の処理
ロビーに入ったらPlayerInfoがプレイヤー数分作成されますが、キャラクター変更ボタンを押した時にボタンのアイコンの変更や他のプレイヤー用のボタンを操作出来ないようにし、自分のプレイヤー用のボタンは操作出来るようにしなければいけません。
そこで、PlayerInfoプレハブに取り付けてあるLobbyPlayerスクリプトの中身に処理を追加していきます。
まずはフィールド部分です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // キャラクター切り替えボタン [SerializeField] private Button changeCharaButton; // 白いEthan用Sprite [SerializeField] private Sprite whiteEthanSprite; // 赤いEthan用Sprite [SerializeField] private Sprite redEthanSprite; // ボタンに表示するキャラクターイメージを文字列で持つ [SyncVar(hook = "OnChangeButtonImage")] public string buttonImageString = "WhiteEthan"; |
キャラクター切り替えボタン、ボタン用のアイコンの種類、ボタンのアイコンに今何が設定されているかの情報をフィールドとして持たせます。
buttonImageStringはSyncVarアトリビュートを取り付け、値の変化があった時に値を同期出来るようにします。
またフックを用いて値が変わった時にOnChangeButtonImageメソッドを呼び出します。
次にOnClientEnterLobbyメソッドの最後に処理を追加します。
1 2 3 | OnChangeButtonImage (buttonImageString); |
OnClientEnterLobbyメソッドはクライアントがロビーに入った時に呼び出されるので、そこでボタンのアイコンを変更します。
次にSetupLocalPlayerメソッドの最後の方のreadyButtonにイベントリスナーを登録した後に4行追加します。
1 2 3 4 5 6 7 8 9 | readyButton.onClick.RemoveAllListeners(); readyButton.onClick.AddListener(OnReadyClicked); changeCharaButton.interactable = true; CmdOnChangeButtonImage (buttonImageString); changeCharaButton.onClick.RemoveAllListeners (); changeCharaButton.onClick.AddListener (OnChangeCharaButton); |
SetupLocalPlayerメソッドはクライアントがロビーに入った時でローカルプレイヤーだった時に実行されます。
ここではキャラクター切り替えボタンの有効化、ボタンが押された時のイベントリスナーにOnChangeCharaButtonを登録し、CmdOnChangeButtonImageメソッドを呼び出してサーバー上でボタンのアイコンを同期させる処理を記述しています。
OnChangeCharaButtonメソッドはボタンを押された時に実行する自前のメソッドです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | // キャラクター変換ボタンを押した時に実行 void OnChangeCharaButton() { var image = changeCharaButton.GetComponent <Image> (); // LobbyManagerのSpawnPrefabsの位置 int index; if (image.sprite == whiteEthanSprite) { image.sprite = redEthanSprite; index = 1; buttonImageString = "RedEthan"; } else { image.sprite = whiteEthanSprite; index = 0; buttonImageString = "WhiteEthan"; } // サーバーでキャラクター切り替え処理 CmdChangeChara (index); // サーバーでCmdOnChangeButtonImageを実行しボタンのイメージを同期する CmdOnChangeButtonImage (buttonImageString); } |
indexはLobbyManagerコンポーネントのRegisterd Spawnable Prefabsに設定した順番と合わせます。
ボタンのアイコンがwhiteEthanSpriteと同じであればredEthanSpriteに切り替えindexを1、それ以外の時はwhiteEthanSpriteにし、indexを0にしています。
CmdChangeCharaはプレイヤーキャラクターを変更した事をサーバーで登録する自前のメソッドです。
CmdOnChangeButtonImageはボタンのアイコンを変更する処理をサーバーで実行する自前のメソッドです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // サーバーでクライアントに対応するキャラクターを割り当てる [Command] void CmdChangeChara(int index) { LobbyManager.s_Singleton.SetPlayer (GetComponent <NetworkIdentity> ().connectionToClient, index); } // ボタンイメージが変更された時に実行するメソッド public void OnChangeButtonImage(string imageString) { if (imageString == "RedEthan") { changeCharaButton.GetComponent <Image> ().sprite = redEthanSprite; } else { changeCharaButton.GetComponent <Image> ().sprite = whiteEthanSprite; } } // サーバーでボタンイメージの変更 [Command] public void CmdOnChangeButtonImage(string imageString) { if (imageString == "RedEthan") { changeCharaButton.GetComponent <Image> ().sprite = redEthanSprite; } else { changeCharaButton.GetComponent <Image> ().sprite = whiteEthanSprite; } buttonImageString = imageString; } |
CmdChangeCharaメソッドではLobbyManagerスクリプトの自前のメソッドであるSetPlayerを呼び出しています。
これはクライアントの情報とボタンのアイコンのindex値を渡してLobbyManagerでデータを保持させます。
OnChangeButtonImageとCmdOnChangebuttonImageはほぼ同じ事をしていますが、OnChangebuttonImageはbuttonImageStringが変更された時にフックで呼び出されるメソッドで、CmdOnChangebuttonImageはサーバーで実行されるメソッドです。
簡略化出来そうですね・・・。
LobbyManagerスクリプトに処理を追加
LobbyPlayerスクリプトからLobbyManagerスクリプトにアクセスし、プレイヤー毎のキャラクターを管理するようにする必要があります。
まずはLobbyManagerスクリプトにフィールドを追加します。
1 2 3 | public Dictionary<int, int> currentPlayers; |
このcurrentPlayersディクショナリはサーバーに接続しているプレイヤーのコネクションIDとRegistered Spawnable Prefabsの登録順を保存するフィールドになります。
つまり、接続したクライアントに対応するプレイヤーキャラクターを保持するものです。
次にOnLobbyServerCreateLobbyPlayerの最初に処理を追記します。
1 2 3 4 5 | if (!currentPlayers.ContainsKey (conn.connectionId)) { currentPlayers.Add (conn.connectionId, 0); } |
OnLobbyServerCreateLobbyPlayerメソッドはロビーに入った時にロビープレイヤー(PlayerInfo)を作成した時にサーバーで呼ばれます。
ここで接続してきた相手のコネクションIDがcurrentPlayersに登録されていなければ、コネクションIDをキーにし、値に0を入れています。
0はRegistered Spawnable Prefabsの0番目のプレハブを設定します(今回はUnityNetworkPlayerを設定予定)。
つまりプレイヤーの初期キャラクター情報を保持しています。
次にSetPlayerメソッドを記述します。
1 2 3 4 5 6 7 8 | // コネクションIDとキャラクターの位置を登録 public void SetPlayer(NetworkConnection conn, int index) { if(currentPlayers.ContainsKey (conn.connectionId)) { currentPlayers[conn.connectionId] = index; } } |
SetPlayerは先ほど記述したLobbyPlayerスクリプトのCmdChangeCharaメソッドから呼び出していました。
currentPlayersにコネクションIDが登録されていれば、ディクショナリにコネクションIDをキーにしてキャラクターのindex値を値として入れています。
次にOnLobbyServerCreateGamePlayerメソッドをオーバーライドして作成します。
1 2 3 4 5 6 7 8 9 10 | // サーバーがゲームプレイヤーを作成する時 public override GameObject OnLobbyServerCreateGamePlayer (NetworkConnection conn, short playerControllerId) { int index = currentPlayers [conn.connectionId]; GameObject playerPrefab = GameObject.Instantiate (spawnPrefabs [index], startPositions [Random.Range (0, startPositions.Count)].position, Quaternion.identity) as GameObject; return playerPrefab; } |
OnLobbyServerCreateGamePlayerメソッドはサーバーで呼び出され、ゲームシーンに移動する時に呼び出されプレイヤーキャラクターを作成します。
ここではcurrentPlayersからコネクションIDに対応するキャラクターの位置であるindexを取得し、そのインデックス値を元にspawnPrefabs配列からプレイヤーキャラクターを取得し、インスタンス化しています。
そのインスタンス化したプレイヤーキャラクターを戻り値とする事で、それぞれのキャラクターが対応するキャラクターに変更されます。
これで機能の作成が出来ましたのでインスペクタで設定をします。
PlayerInfoのLobbyPlayerではPlayerInfoの子要素のChangeCharaButtonや作成したボタン用アイコンを設定します。
LobbyManagerではRegistered Spawnable Prefabsの0番目にUnitynetworkPlayer、1番目にRedPlayerが来るように設定します。
これで設定が出来ましたので、確認してみてください。
終わりに
操作キャラクターの変更方法が全然わからなくて大変でした。(^_^;)
動画主のおかげです。(._.)
ゲーム中のプレイヤーキャラクターの切り替えに関してはNetworkServer.ReplacePlayerForConnectionを使うと出来そうですね。
一からネットワーク接続処理を作っていけば細かい対応も出来そうですが、今のところわたくしには難しいです。(ーー;)