//tips
//PUN UI表示
ボタンが一つの場合のロビーからルームへの入室と同期は正常にできるようになったので、その際のインベントリが共有されてしまう問題について考える。
現状プレイヤー1がとったアイテムがプレイヤー2のインベントリにも表示されてしまうので、その修正方法を考える。
SampleSceneでプレイヤーおよびインベントリは個別に自動生成されるようにしている。
Inventory.csでstaticを使用していることが個別のinventoryが生成できていない原因と考え、別の形への変更を模索する。
単純にstaticを消すだけだと下記のエラーが出てしまう。
Assets/CubeD.cs(18,13): error CS0120: An object reference is required for the non-static field, method, or property 'Inventory.instance'
このpublic static Inventory instance;(public static クラス名.変数名)でどこからでもアクセスすできるようにしているので、staticをなくすと、どのinstanceかわからなくなる。
GameObject.FindGameObjectsWithTagを使用する前に[SerializeField] で対応できないか試してみた。
こうするとアクセス制限がかかり、InventoryUIのUpdateUI()メソッドでアクセスできなくなる。
public GameObject instance;に書き換えてみると、instance = this;でエラーが発生し、型Inventoryをオブジェクトにできないと表示される。
InventoryスクリプトのAwakeメソッド部分を削除した。
/*
void Awake()
{
if (instance == null)
{
instance = this;
}
}
*/
この時点で、InventoryUIスクリプトのInventory.instance.items.CountとCubeDのInventory.instance.Add(item);にエラーが発生する。
Inventoryが実体かどうかで混乱しているので、もう一度アイテム取得の経路を整理する。
まずInventoryオブジェクトがcanvas下の実体として存在し、InventoryスクリプトとInventoryUIスクリプトがそれにアタッチされている状況からはじめる。Inventoryオブジェクト下にはslot要素があり、各slotはslotスクリプトを持ち、InventoryUIのUpdateUIメソッドの中でslots[i].AddItem(Inventory.instance.items[I]);として呼ばれる。
InventoryUIスクリプトでInventory.instanceという仮想収納場所に収納されているアイテム情報を収納されているアイテムの個数分slotに反映させていく。slot[1]に対して、item[1]が対応する。
InventoryParent(slotsparentに指定されている)にGrid Layout Groupを付与することで、うまくslotsの表示順とslot番号を合わせている。
Inventory.instanceは、アイテム情報の収納場所で、gameobjectとは、異なり、ヒエラルキーには表示されないが、インスタンス化はでき、情報の格納場所(この場合は取得したアイテムリスト)として使っている。
とするなら、同じInventoryオブジェクト内にあるInventoryUIは[SerializeField]で対応できるが、オブジェクトとしてシーン上に生成されるCubeDからInventory.instanceへのアクセス手段がpublic static Inventory instance;以外にないのではないか。他に何か手段があるのか検証中。
//ロビーボタンに対する対策
現在削除している下記スクリプトをどう直していくか考える。
問題となっているのは、prefabを絡め、roomlistにより、ルーム項目(ボタン)を管理すると、OnJoinedRoomが1人の入室にもかかわらず2回呼ばれることになる。
Prefabの要素をなくし、初期のルーム項目とその付随ボタンのみのスクリプトにすると正常に遷移する。
なので、prefabのルーム表示管理を行うRoomListViewの修正や遷移を発動させるボタン部分に変更を加えていく。
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);
}
}
まずは削除していたスクリプトを復元し、その上で変更を加えていく。
2人ABでの同期のケースを考える。ロビーに両方入っている状態で、Aが先に入室ボタンを押したら、もう片方のBのロビーにボタンが二つ表示される。
Bが二つのボタンのうち下のボタンを押すと、Aのシーンのには、AとBのプレイヤー2体、Bのシーンには、Bプレイヤー1体が表示される。
Bが二つのボタンのうち上のボタンを押すと、Aのシーンのには、AとBのプレイヤー2体、Bのシーンには、AとBのプレイヤー1体ずつが表示される。
activeEntriesの状態とinactiveEntriesの状態がうまく理解できていないのがRoomListViewをうまく修正できず、迷走している要因なので、それぞれの簡単なシーンを作成し、理解を深めてから再度この問題に戻ってきた方が良さそう。
Dictionary<TKey,TValue>.TryGetValue(TKey, TValue) メソッドとprivate Stack<RoomListEntry> inactiveEntries = new Stack<RoomListEntry>();メソッドの理解が不十分。
activeEntriesは Dictionary型で、これが使用される理由は、stringを合言葉にして、同期さきで入力元と同じボタンを呼び出せることにある。なので、ボタン自体にはphotonViewがつけられ、同期可能なものとなる。
疑問点としては、ここで同期される内容が、名前表示だけでなく、入室者、ボタンごとの遷移先も含むのかということ。activeEntriesには、そこまでの情報が含まれるのか確認。
private Dictionary<string, InputfieldButton> activeEntries = new Dictionary<string, InputfieldButton>();
Stackはプレイヤー間ではなく、ロビーのボタン表示の登録順序に関するもので、ルーム情報のアップデートを受け取り、そのボタン情報の表示順序をロビーのボタン並びの最後尾に配置する。
オブジェクトとして、ヒエラルキーに出てこない形で使われるクラスの変数・実体?に苦戦している感じ。これは数をこなして感覚を掴むしかないかも。