//tips
//Photon同期エラーの検証
2人しかいないはずなのに3つ目のオブジェクトが発生している問題について確認していく。
コンソールのstartメソッドが実行された時に表示されるDebug.Log("0000");を確認する。
先に入室した方では、Debug.Log("0000”);が呼ばれている数は1回だったが、後から入室した方は2回Debug.Log("0000”);が呼ばれていた。
NetworkManagerBスクリプトのJoinOrCreateRoom()メソッドの後に下記のOnJoinedRoom()メソッドの内容が2度繰り返し行われている。
public override void OnJoinedRoom()
{
Debug.Log("OnJoinedRoom");
// 部屋の情報を表示
if (PhotonNetwork.InRoom)
{
Debug.Log("RoomName: " + PhotonNetwork.CurrentRoom.Name);
Debug.Log("HostName: " + PhotonNetwork.MasterClient.NickName);
Debug.Log("Slots: " + PhotonNetwork.CurrentRoom.PlayerCount + " / " + PhotonNetwork.CurrentRoom.MaxPlayers);
}
そして、下の①②③のデバッグ表示を2回繰り返している。
①
ConnectUsingSettings() failed. Can only connect while in state 'Disconnected'. Current state: Connected
UnityEngine.Debug:LogWarning(Object)
Photon.Pun.PhotonNetwork:ConnectUsingSettings(AppSettings, Boolean) (at Assets/Photon/PhotonUnityNetworking/Code/PhotonNetwork.cs:1081)
Photon.Pun.PhotonNetwork:ConnectUsingSettings() (at Assets/Photon/PhotonUnityNetworking/Code/PhotonNetwork.cs:1074)
SampleScene1125:Start() (at Assets/SampleScene1125.cs:11)
②
0000
UnityEngine.Debug:Log(Object)
SampleScene1125:Start() (at Assets/SampleScene1125.cs:13)
③
NullReferenceException: Object reference not set to an instance of an object
InventoryUI.UpdateUI () (at Assets/Script/InventoryUI.cs:18)
Inventory.Start () (at Assets/Script/Inventory.cs:26)
NetworkManagerBのスクリプトの内容がダブっている可能性がある。
もしかしたらprefabでRoomListEntryBのEnterButtonにつけているものと初期配置のボタンで被ってクリックされることになっているのかもしれないと考えて、prefabのボタンを一旦外してみるも結果は変わらず。
先に入室している方に3体、後から入室した方に2体生成され、後から入室した方のプレイヤーが余分に反映されてしまう。
先に入室している方のみがNetworkManagerBのスクリプトの内容をダブって試行している形か。
また別に、OnPlayerEnteredRoom(Player newPlayer)だけではなく、public override void OnJoinedRoom()の遷移も受け取っているのではないかと考え、下記のデバッグログを追加して確認してみる。
if (PhotonNetwork.CurrentRoom.PlayerCount == 2)
{
Debug.Log("OnJoinedRoomCurrentRoom.PlayerCount");
SceneManager.LoadSceneAsync("SampleScene3", LoadSceneMode.Single);
PhotonNetwork.IsMessageQueueRunning = true;
}
Debug.Log("OnJoinedRoomCurrentRoom.PlayerCount”);は呼ばれていなかった。
シーンを再生して観察してみると、先に入室している方の画面の3体のうち1体が自身のプレイヤーで後の2体は後から入室してきたプレイヤーのものであることが、色の変更とscoreの変化からわかった。
ただ、後から入室したプレイヤーのシーンにはきちんと1体ずつしかいないので、先に入室したプレイヤーの後から入室したプレイヤーのデータの取り方がダブっていることになる。
また違和感があったルームのロビーについて再確認する。
二人がロビーにおり、片方が入室ボタンを押した段階で、もう片方のロビーに空のルーム項目が表示される。
その際の挙動をコンソールから見つけると、ルームの人数をinputfieldbuttonスクリプトで表示し、NetworkManagerBのルームリストの更新報告OnRoomListUpdateを呼んでいる。
public void Activate(RoomInfo info)
{
gameObject.SetActive(true);
Debug.Log(info.PlayerCount);
}
public override void OnRoomListUpdate(List<RoomInfo> roomList)
{
Debug.Log("OnRoomListUpdate");
}
ルームリストの更新自体はRoomListViewBの書き部分に記載されている。
public override void OnRoomListUpdate(List<RoomInfo> roomList)
{
foreach (var info in roomList)
{
InputfieldButton entry;
if (activeEntries.TryGetValue(info.Name, out entry))
{
if (!info.RemovedFromList)
{
// リスト要素を更新する
entry.Activate(info);
}
そして、このUpdateが呼ばれた後に2度目のNetworkManagerBのstartメソッドが呼ばれている。
NetworkManagerBがアタッチされているのはRoomListEntryBなのでインスペクターを確認すると、InputfieldButtonスクリプトが2種類あり、一つが正常のもの、もう一つが左端に青いハイライトがつきプラスマークがついたものがある。
新たなシーンを作成し、このハイライト付きのスクリプトを試しに消去してみる。
そうすると後から入室したプレイヤーのシーンにはプレイヤー自身1体しかおらず、先に入室したシーンにはプレイヤー自身と後から入室したプレイヤー2体の合計3体表示されている。
実際に入室させ遷移させる部分ではinputfieldbuttonは特に必要ないので、シーン上から削除し、関連するRoomlistViewBのOnRoomListUpdate(List<RoomInfo> roomList)の中身を実行させないようにしたら正常に同期することができた。
public override void OnRoomListUpdate(List<RoomInfo> roomList)
{
/*
foreach (var info in roomList)
{
InputfieldButton entry;
if (activeEntries.TryGetValue(info.Name, out entry))
{
if (!info.RemovedFromList)
{
// リスト要素を更新する
entry.Activate(info);
}
else
{
// リスト要素を削除する
activeEntries.Remove(info.Name);
entry.Deactivate();
inactiveEntries.Push(entry);
}
}
else if (!info.RemovedFromList)
{
// リスト要素を追加する
entry = (inactiveEntries.Count > 0)
? inactiveEntries.Pop().SetAsLastSibling()
: Instantiate(roomListEntryPrefab, scrollRect.content);
entry.Activate(info);
activeEntries.Add(info.Name, entry);
}
}
*/
}
ロビーのリスト更新が同期エラーに影響していた可能性がある。
ロビーとして機能させるには、リストを使用する必要があるので問題を究明する。
ログを確認すると、
——入室ボタンを押す前
OnJoinedLobby:ロビーに入る
SupportLogger OnRoomListUpdate(roomList). roomList.Count: 0:部屋の数が0
OnRoomListUpdate(List<RoomInfo> roomList):
OnRoomListUpdate:
——入室ボタンを押した後
JoinOrCreateRoom():部屋がない場合、部屋を生成し、入室
SupportLogger OnCreatedRoom(Room: 'Sample' visible,open 1/2 players.). lobby ''[Default]
OnCreatedRoom:部屋の生成を感知
OnJoinedRoom:部屋の入室を感知
ここで面白いのが部屋の生成後にOnRoomListUpdateが呼ばれていないこと。もう一人のプレイヤーも入室させてログの変化を見る。
OnPlayerEnteredRoom:他のプレイヤーが入室したのを感知
Start():Sampleシーンへ遷移
とログが続いていく。
ここで先に入室するとOnRoomListUpdateが表示されないが、後から入室する際には、先にJoinOrCreateRoom()が実行される(他のプレイヤーが入室ボタンを押す)と下記の一連のログが流れるので、自身が部屋を生成する時にはOnRoomListUpdateは呼ばれないことを理解しておく必要がある。
SupportLogger OnRoomListUpdate(roomList). roomList.Count: 1
OnRoomListUpdate(List<RoomInfo> roomList)
OnRoomListUpdate
JoinOrCreateRoom()で部屋がきちんとリストに生成されていること(List<RoomInfo> roomList)のroomListのカウントが一つ増えること)がわかったので、削除した下記のroomlistviewのOnRoomListUpdateの処理を見直していく。
foreachでそれぞれのルームに対して処理を行っており、roomListの中の一つずつをinfoという変数に入れて処理を決めていくことになる。
まず、なぜInputfieldButton entry;が必要になってくるのかをactiveEntriesの理解を通して調べていく。
activeEntriesは Dictionary型で、RoomListViewBスクリプトで下記のように記述されている。
private Dictionary<string, InputfieldButton> activeEntries = new Dictionary<string, InputfieldButton>();
DictionaryではKey(ここではstring)の値を使ってValue(ここではInputfieldButton)の値を取得するもので、roomリストに表記されるボタン名によってそれぞれのルームボタンを管理したいという考え方で使われている。
activeEntries.TryGetValue(info.Name, out entry)の部分にDictionary<TKey,TValue>.TryGetValue(TKey, TValue) メソッドが使用されており、roomlistの元になっているRoomInfoの情報から、room表示ボタンへのアクセスを可能にし、部屋がroomlist内に存在する状態であれば、room表示ボタンをアクティブにする。
もし、roomlistにあるのにルーム名からボタンの実態が取れない場合は、ボタンを追加する必要がある。
foreach (var info in roomList)
{
InputfieldButton entry;
if (activeEntries.TryGetValue(info.Name, out entry))
{
if (!info.RemovedFromList)
{
// リスト要素を更新する
entry.Activate(info);
}
else
{
// リスト要素を削除する
activeEntries.Remove(info.Name);
entry.Deactivate();
inactiveEntries.Push(entry);
}
}
else if (!info.RemovedFromList)
{
// リスト要素を追加する
entry = (inactiveEntries.Count > 0)
? inactiveEntries.Pop().SetAsLastSibling()
: Instantiate(roomListEntryPrefab, scrollRect.content);
entry.Activate(info);
activeEntries.Add(info.Name, entry);
}
}