UnityでPlayFabのロビー機能を使ってみよう

# はじめに

PlayFabではオンラインゲームを作るために必要となる様々な機能が提供されています。
本投稿では、その中のロビー機能に注目して、UnityでPlayFabのロビー機能を利用するための方法について解説していきます。

# 目次
[実行環境](#実行環境)
[SDK導入方法](#sdk導入方法)
[ロビーについての紹介](#ロビーについての紹介)
[　ロビーにあるデータについて](#ロビーにあるデータについて)
[　ロビーのオーナー（所有者）について](#ロビーのオーナー所有者について)
[ロビーの機能一覧](#ロビーの機能一覧)
[　1. PlayFabMultiplayerクラスの初期化](#1-playfabmultiplayerクラスの初期化)
[　2. ロビーを探す](#2-ロビーを探す)
[　　　FileterString](#filterstring)
[　　　SortString](#sortstring)
[　3. ロビーを作成する](#3-ロビーを作成する)
[　　　AccessPolicy](#lobbycreateconfigurationaccesspolicy)
[　　　OwnerMigrationPolicy](#lobbycreateconfigurationownermigrationpolicy)
[　　　SearchProperties](#lobbycreateconfigurationsearchproperties)
[　　　LobbyProperties](#lobbycreateconfigurationlobbyproperties)
[　　　MemberProperties](#lobbyjoinconfigurationmemberproperties)
[　4. ロビーに入室する](#4-ロビーに入室する)
[　5. ロビーから退出する](#5-ロビーから退出する)
[　6. ロビーからメンバーを削除する](#6-ロビーからメンバーを削除する)
[　7. ロビーにあるデータを追加、削除、更新する](#7-ロビーにあるデータを追加削除更新する)
[　　　MemberShipLock](#membershiplock)
[　8. ロビーの情報を取得する](#8-ロビーの情報を取得する)
[　　8-1. ロビーインスタンスを取得したい場合](#8-1-ロビーインスタンスを取得したい場合)
[　　8-2. メンバーの入退出時、ロビー情報更新時に実行したい処理がある場合](#8-2-メンバーの入退出時ロビー情報更新時に実行したい処理がある場合)
[　　8-3. ロビーメンバー全員のEntityを取得したい場合](#8-3-ロビーメンバー全員のentityを取得したい場合)
[　　8-4. ロビーメンバーのメンバーデータを取得したい場合](#8-4-ロビーメンバーのメンバーデータを取得したい場合)
[　　8-5. ロビーメンバーの接続状態を取得したい場合](#8-5-ロビーメンバーの接続状態を取得したい場合)
[　　8-6. ロビーのオーナーの情報を取得したい場合](#8-6-ロビーのオーナーの情報を取得したい場合)
[　　8-7. ロビーデータを取得したい場合](#8-7-ロビーデータを取得したい場合)
[　　8-8. サーチデータを取得したい場合](#8-8-サーチデータを取得したい場合)
[おわりに](#おわりに)
[引用](#引用)
# 実行環境
Windows 10
Unity 2021.3.5f1
PlayFab SDK 2.151.220926
PlayFab Multiplayer SDK 1.3.0.0
# SDK導入方法
1.　まずは下記URLをもとにUnity Editor Extensions SDK を導入する

https://docs.microsoft.com/ja-jp/gaming/playfab/sdks/unity3d/installing-unity3d-sdk

2.　次にPlayFabアカウントを作成してUnity Editor上でログイン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726895/cffdb12d-50f3-36b7-6485-8e8415331ae5.png)
3.　PlayFab SDK導入の案内に従ってインストール
4.  下記Github URLからロビー、マッチング機能を利用するためのPlayFab Multiplayer SDKをインストール。 
※zip形式でダウンロードして解凍する際、.unitypackageファイルの解凍が正常にできない場合があります。その際は、.unitypackageファイルのみを直接ダウンロードしてください。

https://github.com/PlayFab/PlayFabMultiplayerUnity

以上で完了です。

# ロビーについての紹介

ロビー機能は、プレイヤーが一緒にゲームをするための一時的なグループを作成するサービスです。
非常に便利ですが、利用する中で様々なルールがあります。
ルールの詳細は公式リファレンスに記載されてはいますが、思いもよらない所に書かれていることも多く、UnityのPlayFab SDKの使い方の説明も最小限です。
今回、本投稿では私自身が把握しているルールについてまとめて説明していきます。
私もすべてを把握しているわけではないため、コメント等で補足、ご指摘頂けると幸いです。

### ロビーにあるデータについて
ロビーには用途の異なる3種類の独自に定義できるデータがあります。これらを用いることでロビーの検索、入退出管理、情報の共有を行うことができます。
以下が簡単なデータの説明です。
・**メンバーデータ**
　　・メンバーそれぞれに紐づいている個人のデータ。
　　・自分のメンバーデータのみ変更できる。
・**ロビーデータ**
　　・ロビー内のメンバーで共有できるデータ。
　　・ロビーのオーナー（所有者）のみ変更できる。
・**サーチデータ**
　　・ロビーを検索する際のパラメータとして利用できる検索用データ
　　・Key名には「string_key1,string_key2...」と「number_key1,number_key2...」のみ指定可能
　　・ロビーのオーナー（所有者）のみ変更できる。

### ロビーのオーナー（所有者）について
ロビー機能は、オーナーがサーバーのものとクライアントのものがあります。
(2022/9/29)現在、利用可能エンティティに「game_server」がないため、クライアント所有の仕様のみ利用が可能です[1][2]。
そして、クライアント所有のロビーの場合、オーナーには２つの特権が与えられます[3]。
1. メンバーデータを除くすべてのロビー情報を変更できる。
1. ロビーの所有者は新しいオーナーを指名することができる。

**ロビーからオーナーが退出した場合**
オーナーがロビーから退出し、OwnerMigrationPolicy（所有者を移行する際にどのような処理にするのかを決める設定）が新しい所有者を選択できない設定である場合(None,Manualの場合)、ロビーの所有者情報は初期化されます。OwnerMigrationPolicyにはAutomaticというものが選択でき、新しい所有者を自動的に選択します[3]。

**ロビーからオーナーが切断された場合**
オーナーがロビーから切断され、OwnerMigrationPolicyにより、新しいオーナーを選択できない場合、ロビーの所有者権限は初期化されずそのままになってしまいます。

**すべてのメンバーがロビーを離れた場合**
最後のメンバーが退出すると、ロビーは削除されます。

# ロビーの機能一覧

それでは、ロビーを利用するためのUnityのPlayFab Multiplayer SDKの仕様方法について解説していきます。最小限の使用方法については下記の公式ページに説明があります。PlayFabへのログイン等の処理についてはこちらを参考にするのが良いかと思います。また、**本投稿はロビーSDKにある全ての機能を説明するものではなく、使用するために必要となる基本的な機能について説明する**ものです。ロビーからのフレンド招待やマッチング機能との連携等、基本からもう一歩踏み込んだ機能については説明しません。ご了承ください。

https://learn.microsoft.com/en-us/gaming/playfab/features/multiplayer/lobby/lobby-matchmaking-sdks/multiplayer-unity-plugin-quickstart


また、PlayFab Multiplayer SDKのメソッドはPlayFab ロビーAPIにhttpリクエストを送るメソッドであるため、具体的なメソッドや引数についての説明は下記リンクに記載された公式のAPIリファレンスから確認することができます。

https://docs.microsoft.com/ja-jp/rest/api/playfab/multiplayer/lobby?view=playfab-rest

本投稿では、実装例とともに、公式リファレンスでは分かりにくい点を補足しつつ説明していきます。
## 1. PlayFabMultiplayerクラスの初期化
ロビー機能を利用するためには、下記4つの前準備が必要になります。
１．PlayFabへのログイン
２．PlayFabMultiplayerクラスの初期化
３．Updateにロビー（とマッチングの）状態の変化を監視するメソッドを記述する。
４．終了時にPlayFabMultiplayerクラスのリセット
PlayFabのログインについては公式リファレンスや他の記事でも紹介されているためほとんどは割愛しますが、リクエスト成功時に実行する。OnLoginSuccessメソッド内で以下の情報を取得する必要があることだけ記載します。
```C#
private void OnLoginSuccess(LoginResult result)
{
    // はじめてリクエストメソッドを実行するときに必要になる認証情報
    authContext = result.AuthenticationContext
    // 2回目以降にリクエストメソッドを実行するときに必要になる認証情報（UserEntity）
    entityKey = new PFEntityKey(authContext);
}
```
次に残りの２，３，４について説明します。
とは言っても非常に簡単で、インポートした「PlayFabMultiplayerSDK」フォルダ内の「Prefabs」にある「PlayFabMultiplayerEventProcesser」プレファブをHierarchyに置くだけですべての前準備をおこなってくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726895/ebe1826a-f547-64f7-b4d0-de8321e8d440.png)
一応アタッチされているスクリプトの内容も軽く説明します。元から記載されているコメントはそのままにしてあります。
```c#
namespace PlayFab.Multiplayer
{
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class PlayfabMultiplayerEventProcessor : MonoBehaviour
    {
        private void Awake()
        {
            // Exists across scenes for convenience.
            DontDestroyOnLoad(this);
            if (!PlayFabMultiplayer.IsInitialized)
            {
                // PlayFabMultiplayerクラスの初期化
                PlayFabMultiplayer.Initialize();
            }
        }

        private void Update()
        {
            // ロビーやマッチングの情報を取得し、状態に変化があるのかを監視するためのメソッド
            // This object just automatically processes the state changes which in turn triggers
            // the PlayFabMultiplayer.On* events that your other scripts can listen to.
            PlayFabMultiplayer.ProcessLobbyStateChanges();
            PlayFabMultiplayer.ProcessMatchmakingStateChanges();
        }

        private void OnDestroy()
        {
            // PlayFabMultiplayerクラスに登録したイベント等パラメータを削除し、
            // Initialize()を実行する前の状態に戻す。
            PlayFabMultiplayer.Uninitialize();
        }
    }
```
以降のロビー機能のメソッドは上記の前準備が行われている状態で実行しているものとします。
ここで、メソッドを用いるにあたり、１つ注意点があります。これから紹介する**PlayFab Multiplayer SDKのhttpリクエストを送るメソッドは、全て非同期の処理が含まれているため**、メソッドの実行が完了するまでawaitなどで待機させる処理が必要になります。そのため、私はUniTaskを使用して処理が完了するまで待機するように記述しています。
## 2. ロビーを探す
はじめに実装するであろうロビーを検索する機能を紹介します。まずは、実装例を上げて、後から一つ一つパラメータについて説明します。
```C#
public async UniTask FindLobby(string roomName)
{
    // UserEntityについてはこのように作成することもできます。
    UserEntity = new PFEntityKey (PlayFabSettings.staticPlayer.EntityId, PlayFabSettings.staticPlayer.EntityType);
    bool isRequestComplete = false;
    // string_key1はroomeNameと一致していて　かつ
    // ロビーのmembershipLock設定がUnlockになっていて　かつ
    // ロビーに入室している人数が４人以下のルームを検索して、
    // string_key1を基準に昇順に取得
    LobbySearchConfiguration config = new LobbySearchConfiguration
    {
        FilterString = $"(string_key1 eq '{roomName}')"
                        + "(lobby/membershipLock eq 'Unlocked') and "
                        + "(lobby/memberCount lt 4)",
        SortString = "string_key1 asc"
    };
    // OnFindLobbiesSuccessメソッドを定義しても良いですが、このようにラムダ式でハンドラを定義して代入することもできます。
    PlayFabMultiplayer.OnLobbyFindLobbiesCompletedHandler handler = null;
    handler = (IList<LobbySearchResult> searchResults, PFEntityKey newMember, int reason) =>
    {
        if (LobbyError.SUCCEEDED(reason))
        {
            // 成功時の処理
            Debug.Log("ロビー検索成功serachResultsにロビーが格納されています");
            Debug.Log($"ロビーを指定して検索するする時にに必要なロビーID{serachResults[0].LobbyId}")
            Debug.Log($"ロビーに参加する時に必要なConnectionString{serachResults[0].ConnectionString}")
            Debug.Log($"ロビーのオーナー（所有者）のEntityKey{serachResults[0].OwnerEntity}")
            Debug.Log($"ロビーに入室できる最大人数{serachResults[0].MaxMemberCount}")
            Debug.Log($"現在ロビーに入室している人数{serachResults[0].CurrentMemberCount}")
            Debug.Log($"ロビーに設定されているサーチデータ{serachResults[0].SearchProperties}")
        }
        else
        {
            // Error時の処理
            Debug.Log("ロビー検索エラー");
        }
        // 登録したこのメソッドを登録解除する。（この処理をしないと、検索メソッドを実行するたび同じメソッドが多重に登録される）
        PlayFabMultiplayer.OnLobbyFindLobbiesCompleted -= handler;
        isRequestComplete = true;
    };
    // メソッドの登録（この時点ではまだ実行されていない。）
    PlayFabMultiplayer.OnLobbyFindLobbiesCompleted += handler;
    // 非同期メソッドの実行(ログイン時に取得したauthContextとconfigrationクラスが必要)
    PlayFabMultiplayer.FindLobbies(authContext, config);
    // isRequestCompleteがtrueになるまで待つ。
    await UniTask.WaitUntil(() => isRequestComplete);
}
```
LobbyConfigrationクラスについては、メソッドを実行する際に設定するパラメータをまとめたものです。LobbySearchConfigurationでは、ロビーを検索する際の条件とリストの順番を記載します。
#### FilterString
検索するときの条件です。OData言語という少し特殊な記法で条件を記載します。
OData言語の書き方については、公式ページやPlayFabのgitページ[5]に記載されているので参考にしてください。Filterのルールは、
1. 独自に定義できるパラメータのKeyについては、Valueが文字型の場合は「string_key1、string_key2...」と数字型の場合は「number_key1,number_key2...」というKey名のみ利用可能です（ロビー作成の際に定義可能。定義例はロビー作成に記載します）。
2. ロビーが元々もっているパラメータ（ロビーの最大人数maxMemberCount、検索可能になっているかmebershipLock）などを利用する場合は lobby/(パラメータ名)と記載する。利用できるパラメータについてはREST APIの説明ページにあります[6]。

#### SortString
取得したロビーリストをソートする際の設定です。
"(パラメータ名)　asc(or desc)"と記載することでロビーのリストをソートした状態で取得することができます。
## 3. ロビーを作成する
ロビーを作成する機能です。基本的な使い方はロビー検索の際と同じです。

```C#
public async UniTask CreateLobby(string roomName, string userName)
{
    // 作成するロビーの設定
    LobbyCreateConfiguration createConfig = new LobbyCreateConfiguration()
    {
        // 部屋の検索可能設定
        AccessPolicy = LobbyAccessPolicy.Public,
        // ロビーへの最大接続人数の設定
        MaxMemberCount = 4,
        // オーナーが退出した際にオーナー権限をどうするかの設定
        OwnerMigrationPolicy = LobbyOwnerMigrationPolicy.Automatic,
    };

    // ロビーの検索に用いるデータの登録
    createConfig.SearchProperties["string_key1"] = roomName;
    createConfig.SearchProperties["string_key2"] = "hoge";
    createConfig.SearchProperties["number_key1"] = "1234";

    // ロビーメンバーで共有できるロビーデータの登録
    createConfig.LobbyProperties["lobbyName"] = "something";

    // 作成者個人のメンバーデータ
    LobbyJoinConfiguration joinConfig = new LobbyJoinConfiguration();
    joinConfig.MemberProperties["UserName"] = userName;
    joinConfig.MemberProperties["nanigashi"] = "fuga";

    bool isRequestComplete = false;
    PlayFabMultiplayer.OnLobbyCreateAndJoinCompletedHandler handler = null;
    handler = (PlayFab.Multiplayer.Lobby lobby, int result) =>
    {
        if (LobbyError.SUCCEEDED(result))
        {
            Debug.Log($"フレンドマッチ用のロビーの作成に成功しました");
        }
        else
        {
            Debug.Log("ロビー作成失敗");
        }
        PlayFabMultiplayer.OnLobbyCreateAndJoinCompleted -= handler;
        isRequestComplete = true;
    };
    PlayFabMultiplayer.OnLobbyCreateAndJoinCompleted += handler;
    // 返り値にロビーの情報や「退出」や「メンバーの追い出し」、「ロビーの情報更新」等のロビー内で行う重要なメソッドを保有しているLobbyクラスのインスタンスが帰ってきます。
    Lobby ConnectLobby = PlayFabMultiplayer.CreateAndJoinLobby(entity, createConfig, joinConfig);
    await UniTask.WaitUntil(() => isRequestComplete);
}
```
#### LobbyCreateConfiguration.AccessPolicy
ロビー検索を可能にするのかについての設定です。3種類あります。
**Public**：誰でも検索可能な設定です。
**Private**：検索で見つけられない設定です。入室するにはロビーメンバーからの招待が必要です。
**Friend**：ロビーメンバーのフレンドのみ検索可能な設定です。

#### LobbyCreateConfiguration.OwnerMigrationPolicy
ロビーからオーナーが退出した際にオーナー権限をどのようにするかの設定です。ちなみに、CreateAndJoinLobbyで取得したLobbyクラスが持っているGetOwner()を使用することで現在のロビーのオーナーのEntityを取得することができます。
**Automatic**：ロビーにいるメンバーの誰かに自動でオーナー権限を付与する設定です。
**Manual**：オーナーが切断された後は、誰でも自分をオーナーに昇格できるようになる設定です。
**None**：オーナーの接続状況に関係なく、誰でも自分をオーナーに昇格できる設定です。
**Server**：サーバーがロビー所有者であり、メンバーの誰も所有していない設定です。（現在はまだ使用できない）

#### LobbyCreateConfiguration.SearchProperties
検索の際に利用できるサーチデータを登録するものです。Valueが文字の場合は「string_key1、string_key2...」と数字の場合は「number_key1,number_key2...」というKey名のみ利用可能です。number_keyで登録する際、Valueは数字ですが、SerchPropertiesの方がDictionary<string,string>であるため、数値をstring型として登録する必要があります。
例）createConfig.SearchProperties["number_key1"] = "1234";

#### LobbyCreateConfiguration.LobbyProperties
ロビーに入室したメンバー全員で共有できるロビーデータです。サーチデータと異なり、独自のKey名を定義することができます。

#### LobbyJoinConfiguration.MemberProperties
メンバー個人に紐づいたメンバーデータです。ロビーデータと同様に独自のKey名を定義することができます。

## 4. ロビーに入室する
ロビーに参加する機能です。
```C#
public async UniTask JoinLobby(string roomName, string userName)
{
    bool isRequestComplete = false;
    PlayFabMultiplayer.OnLobbyJoinCompletedHandler handler = null;
    handler = (Lobby lobby, PFEntityKey newMember, int reason) =>
    {
        if (LobbyError.SUCCEEDED(reason))
        {
            Debug.Log("ロビーに参加しました！");
        }
        else
        {
            Debug.Log("ロビー参加失敗しました");
        }
        PlayFabMultiplayer.OnLobbyJoinCompleted -= handler;
        isRequestComplete = true;
    };
    // 参加するメンバー自身のメンバーデータ
    Dictionary<string, string> memberData = new Dictionary<string, string>
                {
                    {"UserName", userName },
                    {"nanigashi", "soregashi" }
                };
    PlayFabMultiplayer.OnLobbyJoinCompleted += handler;
    // ロビー検索で取得したConnectionStringをもとに参加するロビーを指定する。
    Lobby ConnectLobby = PlayFabMultiplayer.JoinLobby(entity, ConnectLobbyString, memberData);
    await UniTask.WaitUntil(() => isRequestComplete);
}
```
各パラメータはロビー作成とあまり変わらないので割愛します。

## 5. ロビーから退出する
ロビーから退出するための機能です。
```C#
public async UniTask LeaveLobby()
{
    bool isRequestComplete = false;
    PlayFabMultiplayer.OnLobbyDisconnected += (Lobby lobby) =>
    {
        Debug.Log($"ロビーから退出しました");
        isRequestComplete = true;
    };
    // ロビーの作成、参加時に返り値として取得できるLobbyインスタンスのメソッドから実行できます。
    ConnectLobby.LeaveAllLocalUsers();
    await UniTask.WaitUntil(() => isRequestComplete);
}
```

## 6. ロビーからメンバーを削除する
ロビーからメンバーを強制的に退出させる機能です。ロビーのオーナーのみが実行可能です。
```C#
public async UniTask ForceRemoveMember(PFEntity removeMemberEntity)
{
    bool isRequestComplete = false;
    PlayFabMultiplayer.OnForceRemoveMemberCompletedHandler handler = null;
    handler = (Lobby lobby, PFEntityKey targetMember, int reason) =>
    {
        if (LobbyError.SUCCEEDED(reason))
        {
            Debug.Log($"プレイヤーを強制退出させました");
        }
        else
        {
            Debug.Log($"プレイヤーの強制退出に失敗しました");
        }
        PlayFabMultiplayer.OnForceRemoveMemberCompleted -= handler;
        isRequestComplete = true;
    };
    PlayFabMultiplayer.OnForceRemoveMemberCompleted += handler;
    // キックしたプレイヤーの再入室を拒否するかのbool。trueの場合は再入室不可。
    bool preventRejoin = true;
    // 退出させるメンバーのEntityを指定する。
    ConnectLobby.ForceRemoveMember(removeMemberEntity, preventRejoin);
    await UniTask.WaitUntil(() => isRequestComplete);
}
```

## 7. ロビーにあるデータを追加、削除、更新する
ロビーに設定されているあらゆる情報を更新できる機能です。注意点としてOwnerMigrationPolicyの設定にもよりますが、ロビー情報の更新はロビーのオーナーのみ可能です。また、個人に紐づいたメンバーデータについての更新も可能です。メンバーデータは自分自身のデータのみ更新が可能です。
```C#
public async UniTask PostUpdateLobby()
{
    bool isRequestComplete = false;
    // イベントハンドラーの定義
    PlayFabMultiplayer.OnLobbyPostUpdateCompletedHandler handler = null;
    handler = (Lobby lobby, PFEntityKey localUser, int result) =>
    {
        if (LobbyError.SUCCEEDED(result))
        {
            Debug.Log("ロビー、メンバーの情報更新成功");
        }
        else
        {
            Debug.Log("ロビー、メンバーの情報更新失敗");
        }
        PlayFabMultiplayer.OnLobbyPostUpdateCompleted -= handler;
        isRequestComplete = true;
    };
    PlayFabMultiplayer.OnLobbyPostUpdateCompleted += handler;
    // 更新するデータをまとめたクラス
    LobbyDataUpdate lobbyDataUpdate = new LobbyDataUpdate
    {
        AccessPolicy = LobbyAccessPolicy.Private,
        MaxMemberCount = 4,
        MembershipLock = LobbyMembershipLock.Locked
        // オーナーから別のメンバーに権限の委譲もできます。
        NewOwner = NewOwnerEntity
    };
    // 既に登録されているロビーデータの変更や、新しくロビーデータを登録することも可能
    lobbyDataUpdate.LobbyProperties["NewHoge"] = "NewHogehoge";
    lobbyDataUpdate.LobbyProperties["alreadyRegisteredHoge"] = "ChangeHoge"; 
    // サーチデータも同様に変更可能
    lobbyDataUpdate.SearchProperties["string_key3"] = "NewSearchHoge"; 

    // 更新するメンバーデータをまとめたクラス
    IDictionary<string, string> memberProperties = new IDictionary<string, string>();
    memberProperties["hoge"] = "fuga";

    // 自分のEntityを入れて実行する。
    // lobbyDataUpdateを入れる場合はオーナーでなければ変更できません。
    // メンバーデータのみの場合は自分のメンバーデータを変更することのみ可能です。
    ConnectLobby.PostUpdate(entity, lobbyDataUpdate, memberProperties);
    await UniTask.WaitUntil(() => isRequestComplete);
}
```
#### MembershipLock
ロビーに新しいメンバーが入室できるかどうかを設定できます。
**UnLocked**：ロックされていません。新しいメンバーが自由に入室することができます。
**Locked**：ロックされています。新しいメンバーが入室することはできません。


## 8. ロビーの情報を取得する
自分以外からの変更含むのロビーの状態の情報更新はPlayfabMultiplayerEventProcessorのUpdate内のPlayFabMultiplayer.ProcessLobbyStateChanges()で行われています。ここでは更新されたロビーの情報を取得する方法について紹介します。
### 8-1. ロビーインスタンスを取得したい場合
実はロビー作成や参加メソッドの返り値で取得するLobbyインスタンスはLobby.GetLobbyUsingCache()というメソッドからでも取得することができます。
```C#
// ロビーインスタンスを取得できる。
Lobby ConnectLobby = Lobby.GetLobbyUsingCache();
```
このメソッドを利用することでロビー作成や参加メソッドで取得したインスタンスから参照しにくい箇所からでも利用することができます。
### 8-2. メンバーの入退出時、ロビー情報更新時に実行したい処理がある場合
メンバーが入退出した時や、ロビーの情報が更新された時だけ実行したい処理はあるかと思います。LobbySDKにはそういったロビーの状態が変化した時に実行するメソッドを登録できる仕組みが用意されています。まずは実装例を紹介します。
```C#
// ロビーに入室する前にこのメソッドを実行して、それぞれのイベントを登録しておく必要があります。
// デフォルトでは何も登録されていません。
public void Init()
{
    // 行いたいイベントを登録する。今までの説明と同じでラムダ式で定義してもOK
    PlayFabMultiplayer.OnLobbyMemberAdded += AddMember;
    PlayFabMultiplayer.OnLobbyMemberRemoved += RemoveMember;
    PlayFabMultiplayer.OnLobbyUpdated += UpdateLobby;
}

// メンバーが追加された時に呼び出されます。
private void AddMember(Lobby lobby, PFEntityKey member)
{
    Debug.Log("メンバーを追加しました。");
}

// メンバーもしくは自分が退出した際に呼び出されます。
public void RemoveMember(Lobby lobby, PFEntityKey member, LobbyMemberRemovedReason reason)
{
    Debug.Log("メンバー（もしくは自分）が退出しました。");
    if (reason == LobbyMemberRemovedReason.LocalUserForciblyRemoved)
    {
        // 自分がオーナーに強制的に退出させられた時
    }
    else if(reason == LobbyMemberRemovedReason.RemoteUserLeftLobby)
    {
        // 自分以外の誰かが退出した時
    }
    else if(reason == LobbyMemberRemovedReason.LocalUserLeftLobby)
    {
        // ローカル上にいるプレイヤー（自分）が退出した時
    }
    Debug.Log($"退出理由：{reason}");
}

// ロビーの情報が更新された際に呼び出されます。
public void UpdateLobby(
    Lobby lobby,
    bool ownerUpdated,
    bool maxMembersUpdated,
    bool accessPolicyUpdated,
    bool membershipLockUpdated,
    IList<string> updatedSearchPropertyKeys,
    IList<string> updatedLobbyPropertyKeys,
    IList<LobbyMemberUpdateSummary> memberUpdates) 
{
    Debug.Log("ロビーの情報を更新しました。どの情報が更新されたのかも引数のパラメータから知ることができます");
}
```
2つ注意点があります。実行してみればわかることですが、一応説明しておきます。
1. 自分のいるロビーに誰かが入室してきた場合、
OnLobbyMemberAdded
OnLobbyUpdated
と２つ呼ばれます。考えてみれば当たり前ですが、メンバーが増えることも情報更新に含まれますので注意が必要です。退出時に関しても同様にOnMemberRemovedとOnLobbyUpdatedが両方呼ばれます。
2. 既に複数人いるロビー（今回の例では2人いるロビーとします）に入室した場合、
OnLobbyMemberAdded
OnLobbyUpdated
OnLobbyMemberAdded
OnLobbyUpdated
と入室時に追加、更新メソッドが2回ずつ実行されます。これはメンバーの追加処理が1人ずつ行われており、その処理ごとに追加と更新のメソッドが呼び出されるためです。

### 8-3. ロビーメンバー全員のEntityを取得したい場合
ロビー作成や参加メソッドの返り値で取得できるLobbyインスタンスにあるGetMembers()を用いることで取得できます。
```C#
// ロビーメンバーのEntityのリストを取得できる。
IList<PFEntityKey> ConnectMembersEntity　= ConnectLobby.GetMembers();
```

### 8-4. ロビーメンバーのメンバーデータを取得したい場合
ロビーメンバーのEntityを用いてLobbyインスタンスにあるGetMemberProperties(entity)で取得することができます。
```C#
// メンバーデータを1人ずつ取得することができます。
IDictionary<string, string> memberData = ConnectLobby.GetMemberProperties(memberEntity);
string memberName = memberData["UserName"];
```

### 8-5. ロビーメンバーの接続状態を取得したい場合
ロビーメンバーのEntityを用いてLobbyインスタンスにあるGetMemberConnectionStatus(entity)で取得することができます。
```C#
// メンバーの接続状態を1人ずつ取得することができます。
// statusには「LobbyMemberConnectionStatus.NotConnected」か「LobbyMemberConnectionStatus.Connected」のどちらかが格納されます。
LobbyMemberConnectionStatus status = ConnectLobby.GetMemberConnectionStatus(memberEntity);
```

### 8-6. ロビーのオーナーの情報を取得したい場合
ロビーメンバーのEntityを用いてLobbyインスタンスにあるTryGetOwner(out PFEntityKey owner)でオーナーがいるかどうかと、オーナーがいる場合はオーナーのEntityを取得することができます。
```C#
// existOwnerにはオーナーがいるかどうかのboolが返ってきます。(オーナーがいる場合はtrue)
// そしてオーナーがいる場合にはownerにオーナーのentityが格納されます。オーナーがいない場合にはnullが格納されます。
bool existOwner = ConnectLobby.TryGetOwner(out PFEntityKey owner);
```

### 8-7. ロビーデータを取得したい場合
LobbyインスタンスにあるGetLobbyProperties()で取得できます。
```C#
// ロビーに登録されているロビーデータをIDict型で取得できる。
IDictionary<string, string> lobbyData = ConnectLobby.GetLobbyProperties();
```

### 8-8. サーチデータを取得したい場合
LobbyインスタンスにあるGetSearchProperties()で取得できます。
```C#
// ロビーに登録されているサーチデータをIDict型で取得できる。
IDictionary<string, string> searchData = ConnectLobby.GetSearchProperties();
```

# おわりに
基本的な機能の説明は以上です。
ロビーには「ロビーへの招待機能」や「PlayFabのマッチング機能と連携してマッチングした人と同じロビーに入室する機能」など今回紹介した機能以外にもいくつか機能が存在します。今後時間があればマッチング機能回りも紹介したいと思います。
本投稿が少しでもロビー機能を実装する際の手助けになれば幸いです。

# 引用
[1]https://community.playfab.com/questions/62439/how-can-i-get-a-server-to-be-of-entity-type-game-s.html
[2]https://learn.microsoft.com/ja-jp/gaming/playfab/features/data/entities/available-built-in-entity-types
[3]https://docs.microsoft.com/ja-jp/gaming/playfab/features/multiplayer/lobby/owner-requirements-and-privileges
[4]https://docs.microsoft.com/ja-jp/gaming/playfab/features/multiplayer/lobby/ownership-changes
[5]https://github.com/MicrosoftDocs/playfab-docs/blob/docs/playfab-docs/features/multiplayer/lobby/find-lobbies.md
[6]https://learn.microsoft.com/ja-jp/rest/api/playfab/multiplayer/lobby/find-friend-lobbies?view=playfab-rest