This document is about: SERVER 4
SWITCH TO

マッチメイキングガイド

Photonではルームに入って誰かとプレイ(または対戦)することは非常に簡単です。
基本的には3つの方法があります:
マッチするルームをサーバーに探させる方法、フレンドについていってそのルームに入る方法、ルームリストを取得してユーザーに選ばせる方法です。
Photonはこれら3つの方法をサポートしますが、独自の方法を生み出すことも可能です。

弊社は、迅速で簡単なマッチメイキングが多くのゲームに最適であると考えています。このため、ランダムマッチメイキングを使用し、スキルやレベルなどのフィルタを追加するよう推奨しています。

マッチメイキングのチェックリスト

プレイヤーのマッチングに問題がある場合には、以下のチェックリストを参照してください:

  • クライアントが同じマスターサーバーに接続している点を確認してください。使用しているデバイスやプラットフォームに関わらず、同じマスターサーバーに接続しているプレイヤー同士のみがプレイできます。
  • プレイヤーが異なるUserIdを保有している点を確認してください。
    同じUserIdのプレイヤーは同じルームに参加できません。
  • 名前でルームに参加しようとする前に、このルームが作成されているかを確認してください。
    またはJoinOrCreateRoomを使用してください。
  • ランダムなルームに参加しようとする場合には、作成時に同じロビー(名前とタイプ)を使用するよう選択する点を確認してください。
  • ルームプロパティをフィルタとして使用してランダムマッチメイキングをおこなう場合、ルーム作成時にこれらのプロパティのキーをロビーから見えるように設定してください。
  • SQLフィルタでランダムマッチメイキングをおこなう場合、使用する予約済みのフィルタリングプロパティキーを、ロビーから見えるように設定してください。
    ランダムマッチメイキングが試行されるたびにSQLフィルタの条件を緩めるか、連鎖フィルタを試用する、または試行が数回失敗した時点で新しいルームを作成することも重要です。
  • 非同期マッチメイキングを実装する場合には適切な設定でWebhookを使用する(「AsyncJoin」を有効にする)点、またはAsyncRandomLobbyを使用する点に留意してください。

クイックマッチ

ほとんどのプレイヤーは、すぐにゲームを開始したいので、
速やかにマッチに参加することを望んでいます。

そのため、多くのゲームは最初のモードとしてクイックマッチを用意しています。

ここで提案するワークフローでは、プレイヤーが長いリストからルームを選ぶことなく、すぐにルームに入れるようにします。

プレイヤーをすぐにルームに入れる方法は以下のとおりです。

JoinRandomOrCreateRoom

JoinRandomOrCreateRoomを呼び出すだけです(正確な関数またはメソッド名は、クライアントSDKによって異なる場合があります)。
ルームが見つかった場合はそれに参加して、それ以外の場合は新しいルームが作成されます。

C#

using Photon.Realtime;
using System.Collections.Generic;

public class QuickMatchExample : IMatchmakingCallbacks
{
    private LoadBalancingClient loadBalancingClient;

    private void QuickMatch()
    {
        loadBalancingClient.OpJoinRandomOrCreateRoom(null, null);;
    }

    // do not forget to register callbacks via loadBalancingClient.AddCallbackTarget
    // also deregister via loadBalancingClient.RemoveCallbackTarget
    #region IMatchmakingCallbacks

    void IMatchmakingCallbacks.OnJoinedRoom()
    {
        // joined a room successfully
    }

    // [..] Other callbacks implementations are stripped out for brevity, they are empty in this case as not used.

    #endif
}

JoinRandomRoom または CreateRoom

  • JoinRandomRoomを試してください(正確な関数またはメソッド名は、クライアントSDKによって異なる場合があります)。
    • それだけで完了する場合もあります。
      クライアントが正常にルームに参加します。
    • ルームが存在しないか、参加できない場合もあります(閉じている、見えない、または満室)。
  • ルームがすぐに見つからない場合は、作成します。
    • ルームの名前を表示しない場合は、名前を作成しないでください。
      これはサーバーに任せます。
      ルームの作成時に、「ルーム名」としてnullまたは空の文字列を設定します。
      ルームは一意のGUIDを取得します。
    • 「最大プレーヤー」の値を適用します。
      このように、ルームがいっぱいになると、サーバーは最終的にプレイヤーの追加を停止します。
  • クライアントがルームに一人でいる場合(プレーヤー数== 1):
    待機します。
    対戦相手を待っていることを示す画面を表示します。
  • 十分な数のプレイヤーがそろえば、ゲームを始めることもできます。
    新しいプレイヤーを締め出すには、ルームを閉じます。
    ルームが満室でなくても、サーバーはルームにプレイヤーを入れるのをやめます。
    • 注:ルームを閉じても、少しの間、入りかけていたプレイヤーが参加する可能性があります。
      クライアントがルームを閉じた後に、プレイヤーが参加しても驚かないでください。

C#

using Photon.Realtime;
using System.Collections.Generic;

public class QuickMatchExample : IMatchmakingCallbacks
{
    [SerializeField]
    private maxPlayers = 4;
    private LoadBalancingClient loadBalancingClient;

    private void CreateRoom()
    {
        RoomOptions roomOptions = new RoomOptions();
        roomOptions.MaxPlayers = maxPlayers;
        EnterRoomParams enterRoomParams = new EnterRoomParams();
        enterRoomParams.RoomOptions = roomOptions;
        loadBalancingClient.OpCreateRoom(enterRoomParams);
    }

    private void QuickMatch()
    {
        loadBalancingClient.OpJoinRandomRoom();
    }

    // do not forget to register callbacks via loadBalancingClient.AddCallbackTarget
    // also deregister via loadBalancingClient.RemoveCallbackTarget
    #region IMatchmakingCallbacks

    void IMatchmakingCallbacks.OnJoinRandomFailed(short returnCode, string message)
    {
        CreateRoom();
    }

    void IMatchmakingCallbacks.OnJoinedRoom()
    {
        // joined a room successfully
    }

    // [..] Other callbacks implementations are stripped out for brevity, they are empty in this case as not used.

    #endif
}

このワークフローを使用するば、プレイヤーは簡単にゲームに参加できます。

ランダムマッチメイキング

プレイヤーが、クイックマッチではなく、特定のマップまたはモード(2対2など)をプレイしたい場合もあります。

任意の「カスタムルームプロパティ」を設定し、それらをJoinRandomRoomリクエストのフィルターとして使用できます(正確な関数またはメソッド名は、クライアントSDKによって異なる場合があります)。

ロビーでいくつかのプロパティを公開する

カスタムルームプロパティは、ルーム内のすべてのプレーヤーに同期され、現在のマップ、ゲームモード、難易度、ターン、ラウンド、開始時間などを追跡するのに役立ちます。
文字列キーを持つハッシュテーブルとして扱われます。

デフォルトでは、無駄を抑えるため、これらのプロパティは部屋の内部でのみアクセスでき、マスターサーバー(ロビーが存在する場所)には送信されません。
ロビーに公開するカスタムルームプロパティを選択できます。
これらのプロパティはランダムなマッチメイキングのフィルターとして使用され、ロビーに表示されます(ルームリストのルーム情報の一部として送信されます。デフォルトタイプのロビーのみがルームにリストを送信します。)

:

「マップ」と「gm」をマッチメイキングに使用できるようにするためには、ルームを作成するときに「ロビーに表示されるルームプロパティ」のリストを設定できます。
ヒント:簡潔な名前が望ましいので、「GameMode」の代わりに「gm」を使用してください。

C#

using Photon.Realtime;
using System.Collections.Generic;
using Hashtable = ExitGames.Client.Photon.Hashtable;

public class CreateRoomWithLobbyPropertiesExample : IMatchmakingCallbacks
{
    public const string MAP_PROP_KEY = "map";
    public const string GAME_MODE_PROP_KEY = "gm";
    public const string AI_PROP_KEY = "ai";

    private LoadBalancingClient loadBalancingClient;

    private void CreateRoom()
    {
        RoomOptions roomOptions = new RoomOptions();
        roomOptions.CustomRoomPropertiesForLobby = { MAP_PROP_KEY, GAME_MODE_PROP_KEY, AI_PROP_KEY };
        roomOptions.CustomRoomProperties = new Hashtable { { MAP_PROP_KEY, 1 }, { GAME_MODE_PROP_KEY, 0 } };
        EnterRoomParams enterRoomParams = new EnterRoomParams();
        enterRoomParams.RoomOptions = roomOptions;
        loadBalancingClient.OpCreateRoom(enterRoomParams);
    }

    // do not forget to register callbacks via loadBalancingClient.AddCallbackTarget
    // also deregister via loadBalancingClient.RemoveCallbackTarget
    #region IMatchmakingCallbacks

    void IMatchmakingCallbacks.OnCreateRoomFailed(short returnCode, string message)
    {
       // log error message and code
    }

    void IMatchmakingCallbacks.OnCreatedRoom()
    {
    }

    void IMatchmakingCallbacks.OnJoinedRoom()
    {
        // joined a room successfully, OpCreateRoom leads here on success
    }

    // [..] Other callbacks implementations are stripped out for brevity, they are empty in this case as not used.

    #endif
}

「ai」には最初は値がないことに注意してください。
ルームに設定されるまでロビーには表示されません(C#SDKでは、これは Room.SetCustomPropertiesを介して行われます)。
「map」、「gm」、「ai」の値を変更すると、ロビーで少し遅れて更新されます。

後で(ルーム作成後)、ロビーに表示されるルームプロパティキーを変更(追加または削除)することもできます(C#SDKでは、これは Room.PropertiesListedInLobbyを介して行われます)
ヒント:ロビーのプロパティのリストを短くして、ルームに参加するとき、またはロビーに参加するときにクライアントのパフォーマンスに問題が起きないようにしてください(デフォルトタイプのロビーのみがルームリストを送信します)。

繰り返しますが、このためにロビーに参加する必要はありません(非常に長いルームリストを取得する必要はありません)。
ロビーに設定すると、フィルターとしても利用できるようになります。

Join Randomでのルームプロパティのフィルタリング

このセクションでは、SQL Lobbyタイプを除外しています。 SQLのマッチメイキングの詳細がこちらをご覧ください。

ランダムなルームを見つけようとするとき、必要に応じて、予想されるルームのプロパティまたは予想される最大プレーヤーを選択できます。
これらは、サーバーが適切なルームを選択するためのフィルターとして機能します。

:

C#

using Photon.Realtime;
using System.Collections.Generic;
using Hashtable = ExitGames.Client.Photon.Hashtable;

public class RandomMatchmakingExample : IMatchmakingCallbacks
{
    public const string MAP_PROP_KEY = "map";

    private LoadBalancingClient loadBalancingClient;

    public void JoinRandomRoom(byte mapCode, byte expectedMaxPlayers)
    {
        Hashtable expectedCustomRoomProperties = new Hashtable { { MAP_PROP_KEY, mapCode } };
        OpJoinRandomRoomParams opJoinRandomRoomParams = new OpJoinRandomRoomParams();
        opJoinRandomRoomParams.ExpectedMaxPlayers = expectedMaxPlayers;
        opJoinRandomRoomParams.ExpectedCustomRoomProperties = expectedCustomRoomProperties:
        loadBalancingClient.OpJoinRandomRoom(opJoinRandomRoomParams);
    }

    // do not forget to register callbacks via loadBalancingClient.AddCallbackTarget
    // also deregister via loadBalancingClient.RemoveCallbackTarget
    #region IMatchmakingCallbacks

    void IMatchmakingCallbacks.OnJoinRandomFailed(short returnCode, string message)
    {
        // log error code and message
        // here usually you create a new room
    }

    void IMatchmakingCallbacks.OnJoinedRoom()
    {
        // joined a room successfully, OpJoinRandomRoom leads here on success
    }

    // [..] Other callbacks implementations are stripped out for brevity, they are empty in this case as not used.

    #endif
}

より多くのフィルタプロパティを渡す場合には、ルームがこれらのプロパティにマッチングする可能性は低くなります。
オプションは制限したようがよいでしょう。

ロビーで既知ではないプロパティをフィルタリングしないよう、留意してください(上記を参照ください)。

フレンドと一緒にプレイ

ユーザーがフレンドと(たとえばPhoton Chatなどで)コミュニケーションをとっている場合、容易にルームの作成をおこなうことができます。また、OpJoinOrCreateRoomを使用すれば誰でもそのルームに参加することができます。

:

一意のルーム名は、たとえば「friendName1 + friendName2 + randomInteger」のように構成できます。

他のユーザーが参加しないようにするには、次のように非表示のルームを作成します。

C#

using Photon.Realtime;
using System.Collections.Generic;

public class PrivateRoomExample : IMatchmakingCallbacks
{
    private LoadBalancingClient loadBalancingClient;

    public void JoinOrCreatePrivateRoom(string nameEveryFriendKnows)
    {
        RoomOptions roomOptions = new RoomOptions();
        roomOptions.IsVisible = false;
        EnterRoomParams enterRoomParams = new EnterRoomParams();
        enterRoomParams.RoomName = nameEveryFriendKnows;
        enterRoomParams.RoomOptions = roomOptions;
        loadBalancingClient.OpJoinOrCreateRoom(enterRoomParams);
    }

    // do not forget to register callbacks via loadBalancingClient.AddCallbackTarget
    // also deregister via loadBalancingClient.RemoveCallbackTarget
    #region IMatchmakingCallbacks

    void IMatchmakingCallbacks.OnJoinRoomFailed(short returnCode, string message)
    {
      // log error code and message
    }

    void IMatchmakingCallbacks.OnJoinedRoom()
    {
        // joined a room successfully, OpJoinOrCreateRoom leads here on success
    }

    // [..] Other callbacks implementations are stripped out for brevity, they are empty in this case as not used.

    #endif
}

一意のユーザーIDを使用する場合は、 FindFriendsを使用して友達を探すこともできます(正確な関数またはメソッド名は、クライアントSDKによって異なる場合があります)。

ルームでUserIDをパブリッシュ

Photonでは、様々な場面でUserIDを使用します。
たとえば、フレンドを見つけるにはプレイヤーごとに適切なUserIDを使用します。
Photonには、ルームでプレイヤーのuserIDを確認できるオプションがあります。
そのためにはルームを作成するときに RoomOptions.PublishUserIdtrueに設定します。
サーバーはUserIDを提供し、クライアント上でのアクセスが可能になります。
C# SDKでは、Player.UserIdによってこの処理がおこなわれます。

注:

  • UserIDは、Photonの参加イベント内で、プレイヤープロパティとともにブロードキャストされます。
  • クライアント用のUserIDを設定するには、以下の3つの方法があります:
  1. 接続前に、クライアントがUserIDを設定します。
  2. カスタム認証を使用して、外部ウェブサービスによって返されます。
    これは、クライアントによって送信された値をオーバーライドします。
  3. Photonは明示的にUserIDs (GUID)を設定しないユーザーにUserIDs (GUID)を生成します。
  • 一般的に、UserIDは表示されません。

マッチメイキングスロットの予約

プレイヤーはフレンドも同様に参加することを分かった上でルームに入る場合があります。
スロット予約を使用すると、Photonは特定のユーザー用にスロットをブロックし、マッチメイキングの際に考慮します。
スロットを予約するには、ルームに参加する際のメソッド(JoinRoomJoinOrCreateRoomJoinRandomRoomおよびCreateRoom)で取得するexpectedUsersパラメータがあります。

C#

EnterRoomParams enterRoomParams = new EnterRoomParams();
enterRoomParams.ExpectedUsers = expectedUsers;
// create room example
loadBalancingClient.OpCreateRoom(enterRoomParams);
// join room example
loadBalancingClient.OpJoinRoom(enterRoomParams);
// join or create room example
loadBalancingClient.OpJoinOrCreateRoom(enterRoomParams);
// join random room example
OpJoinRandomRoomParams opJoinRandomRoomParams = new OpJoinRandomRoomParams();
opJoinRandomRoomParams.ExpectedUsers = expectedUsers;
loadBalancingClient.OpJoinRandomRoom(opJoinRandomRoomParams);

誰かが参加すると分かっている場合は、UserIDの配列を渡します。
JoinRandomRoomの場合、サーバーはあなたと参加を予定しているプレイヤー(また、既にルームにいるアクティブなプレイヤーや予測されるプレイヤー)のために十分なスロットのあるルームを探そうとします。
現在のexpectedUsersが変更された場合、サーバーはルーム内のクライアントに対してそれを更新します。

ルーム内の予想されるユーザーのリストを更新できます(1人以上のユーザーを追加または削除します)。これは、よく知られたルームプロパティを介して行われます。
(C#SDKでは、 Room.ExpectedUsersを取得および設定できます)。

スロット予約に対応するには、ルーム内のUserIDのパブリッシュを有効化する必要があります。

使用例:チームマッチメイキング

これは、マッチメイキングでチームをサポートする際に使用できます。
チームのリーダーが実際のマッチメイキングをおこないます。
リーダーがルームに参加し、すべてのメンバーにスロットを予約できます:

ランダムルームを検索するには:

C#

OpJoinRandomRoomParams opJoinRandomRoomParams = new OpJoinRandomRoomParams();
opJoinRandomRoomParams.ExpectedUsers = teamMembersUserIds;
loadBalancingClient.OpJoinRandomRoom(opJoinRandomRoomParams);

なにも見つからない場合には、新規作成します:

C#

EnterRoomParams enterRoomParams = new EnterRoomParams();
enterRoomParams.ExpectedUsers = teamMembersUserIds;
loadBalancingClient.OpCreateRoom(enterRoomParams);

他のプレイヤーはマッチメイキングをする必要はありませんが、以下を繰り返し呼ぶ必要があります:

C#

loadBalancingClient.OpFindFriends(new string[1]{ leaderUserId });

リーダーがルームに到着すると、FindFriends オペレーションによってルーム名が明らかになり、すべてのプレイヤーがそのルームに参加可能となります:

C#

EnterRoomParams enterRoomParams = new EnterRoomParams();
enterRoomParams.RoomName = roomNameWhereTheLeaderIs;
loadBalancingClient.OpJoinRoom(enterRoomParams);

ロビー

Photonは、「ロビー」でルームを管理します。
すべてのルームはロビーに属しています。
ロビーは、名
名前には任意の文字列を使用できますが、ロビーには、DefaultSQLAsyncの3つのタイプしかありません。
それぞれに、特定のユースケースに適した独自の機能があります。

すべてのアプリケーションは、既存のロビーデフォルトのロビーから始まります。
ほとんどのアプリケーションは他のロビーを必要としません。
クライアントはその場で他のロビーを作成することもできます。
操作リクエストで次の新しいロビー定義を指定すると、ロビーが存在し始めます: JoinLobby CreateRoomまたは JoinOrCreateRoom

ルームと同様に、ロビーにも参加することができます。
ロビーでは、クライアントはそのロビー内のルームリストのみを取得します。
他にはなにも取得しません。
また、ロビーでは他のプレイヤーと通信することはできません。

クライアントがロビーに参加し、明示的にロビーを設定せずにルームを作成(またはJoinOrCreate)しようとして、作成が成功または発生すると、ルームは現在参加しているロビーに追加されます。
クライアントがロビーに参加しておらず、明示的にロビーを設定せずにルームを作成(またはJoinOrCreate)しようとした場合、作成が成功または発生すると、ルームはデフォルトのロビーに追加されます。
クライアントがロビーに参加し、ロビーを明示的に設定してルームの作成(またはJoinOrCreate)を試行して、作成が成功または発生した場合:

  • ロビー名がnullまたは空の場合:ルームは現在参加しているロビーに追加されます。
    つまり、カスタム/異なる部屋に参加している場合、デフォルトのロビーにルームを作成することはできません。
  • ロビー名がnullでも空でもない場合:ルームは、ルーム作成リクエストで指定されたロビーに追加されます。

クライアントがロビーに参加していて、明示的にロビーを設定せずにランダムルームに参加しようとすると、サーバーは現在参加しているロビーでルームを探します。
クライアントがロビーに参加しておらず、明示的にロビーを設定せずにランダムルームに参加しようとすると、サーバーはデフォルトのロビーでルームを探します。
クライアントがロビーに参加し、ロビーを明示的に設定してランダムルームに参加しようとした場合:

  • ロビー名がnullまたは空の場合:サーバーは現在参加しているロビーのルームを探します。
    つまり、カスタム/異なるルームに参加している場合、デフォルトのロビーのランダムなルームに参加することはできません。
  • ロビー名がnullでも空でもない場合:サーバーは、ルーム作成リクエストで指定されたロビーでルームを探します。

クライアントがあるロビーに参加していて、別のロビーに切り替えたい場合、JoinLobbyを直接呼び出すことができ、LeaveLobbyを明示的に呼び出して最初の部屋を離れる必要はありません。

デフォルトのロビータイプ

このタイプは同期 ランダムマッチメイキングに最適です。
高度な機能はありませんが、もっとも一般的に使用されるタイプです。

デフォルトのロビータイプに参加している間、クライアントは定期的にルームリストの更新を受け取ります。

デフォルトのロビータイプに参加すると、クライアントはルームリストのアップデートを定期的に受信します。
その後、クライアントは定期的にルームリストの更新を受け取ります。

リストは、オープンまたはクローズ、フルかどうかの2つの基準を使用してソートされます。
したがって、リストは次の3つのグループで構成されています。

  • 最初のグループ:オープンで満室ではない(参加可能)。
  • 2つ目のグループ:満室ですが、閉じていません(参加不可)。
  • 3つ目のグループ:クローズ(参加不可、満室でも、そうでなくても)。

各グループでは、エントリに特定の順序はありません(ランダム)。

ルーム(またはルームの更新)のリストも数に制限があります。Lobby Limitsを参照してください。

C#

using Photon.Realtime;
using System.Collections.Generic;

public class RoomListCachingExample : ILobbyCallbacks, IConnectionCallbacks
{
    private TypedLobby customLobby = new TypedLobby("customLobby", LobbyType.Default);
    private LoadBalancingClient loadBalancingClient;

    private Dictionary<string, RoomInfo> cachedRoomList = new Dictionary<string, RoomInfo>();

    public void JoinLobby()
    {
        loadBalancingClient.JoinLobby(customLobby);
    }

    private void UpdateCachedRoomList(List<RoomInfo> roomList)
    {
        for(int i=0; i<roomList.Count; i++)
        {
            RoomInfo info = roomList[i];
            if (info.RemovedFromList)
            {
                cachedRoomList.Remove(info.Name);
            }
            else
            {
                cachedRoomList[info.Name] = info;
            }
        }
    }

    // do not forget to register callbacks via loadBalancingClient.AddCallbackTarget
    // also deregister via loadBalancingClient.RemoveCallbackTarget
    #region ILobbyCallbacks

    void ILobbyCallbacks.OnJoinedLobby()
    {
        cachedRoomList.Clear();
    }

    void ILobbyCallbacks.OnLeftLobby()
    {
        cachedRoomList.Clear();
    }

    void ILobbyCallbacks.OnRoomListUpdate(List<RoomInfo> roomList)
    {
        // here you get the response, empty list if no rooms found
        UpdateCachedRoomList(roomList);
    }

    // [..] Other callbacks implementations are stripped out for brevity, they are empty in this case as not used.

    #endif

    #region IConnectionCallbacks

    void IConnectionCallbacks.OnDisconnected(DisconnectCause cause)
    {
        cachedRoomList.Clear();
    }

// [..] Other callbacks implementations are stripped out for brevity, they are empty in this case as not used.

#endregion
}

デフォルトのロビー

名前はnullで、タイプは[Default Lobby Type](#default_lobby_type)です。
C#SDKでは、 TypedLobby.Defaultで定義されています。
デフォルトのロビーの名前は予約されています。
デフォルトのロビーだけがnullを名前に持つことができます。他のロビーには、nullや空以外の名前の文字列が必要です。
空またはnullの文字列をロビー名として使用する場合、指定されたタイプに関係なく、デフォルトのロビーを指します。

推奨フロー

絶対に必要な場合を除いて、ロビーに参加しないとをお勧めします。
必要に応じて、特定のロビーまたはカスタムロビーにルームを追加する場合、クライアントは新しいルームを作成するときにロビーを指定できます。

デフォルトタイプのロビーに参加すると、ルームのリストが表示されますが、ほとんどの場合は役に立ちません。

  • リストのエントリ間でpingに関して違いはありません。
  • 通常、プレイヤーはクイックマッチを探しています
  • ルームリストを受信すると、遅延が追加され、トラフィックが消費されます
  • 情報が多すぎる長いリストは、ユーザーエクスペリエンスに悪影響を与える可能性があります

代わりに、プレイヤーにマッチメイキングをより細かく制御するには、ランダムマッチメイキングにフィルターを使用します。
複数のロビーは(サーバー側の)ランダムマッチメイキングでも使用され、ロビー統計を利用できるため、今でも有用です。

SQLロビータイプ

SQLロビータイプでは、 JoinRandomRoomの文字列フィルターが、デフォルトの予期されるロビープロパティを置き換えます。
また、SQLロビータイプでは、1つのMatchmakingModeのみがサポートされています: FillRoom(default,0)。
カスタムルームリストは、デフォルトのロビータイプにのみ存在する自動定期ルームリストを置き換えます。

このロビータイプは、サーバー側で使用される完全にクライアント主導の スキルベースのマッチメイキング、より高度なマッチメイキングフィルタリングを追加します。

内部的には、SQLロビーは、SQLiteテーブル内のルームを最大10の特別な「フィルタリングプロパティ」としてリスト化します。
現在、これらの命名は"C0"、"C1"から"C9"までとして固定化されています。
整数型および文字列型の値のみが許可され、値が特定のロビーのコラムに割り当てられると、このコラムはその型の値にロックされます。
静的な命名であるにもかかわらず、クライアントはロビーにどちらが必要かを定義する必要があります。
またロビーに見えるか、もしくは見えないかという「Cx」以外のカスタムルームプロパティを使用することも可能です。

ルームの作成中または参加後に、ロビーに表示または非表示のSQLプロパティ以外のカスタムルームプロパティを引き続き使用できます。
ただし、マッチメイキングには使用されません。

クエリはJoinRandomRoom操作で送信できます。
フィルタリングクエリは、基本的には "C0" .. "C9"値に基づくSQL WHERE条件です。
SQLiteでサポートされているすべての演算子のリストとその使用方法については、こちらをご覧ください。
除外キーワードを考慮してください。

例:

C#

using Photon.Realtime;
using System.Collections.Generic;
using Hashtable = ExitGames.Client.Photon.Hashtable;

public class RandomMatchmakingExample : IMatchmakingCallbacks
{
    public const string ELO_PROP_KEY = "C0";
    public const string MAP_PROP_KEY = "C1";
    private TypedLobby sqlLobby = new TypedLobby("customSqlLobby", LobbyType.SqlLobby);
    private LoadBalancingClient loadBalancingClient;

    private void CreateRoom()
    {
        RoomOptions roomOptions = new RoomOptions();
        roomOptions.CustomRoomProperties = new Hashtable { { ELO_PROP_KEY, 400 }, { MAP_PROP_KEY, "Map3" } };
        roomOptions.CustomRoomPropertiesForLobby = { ELO_PROP_KEY, MAP_PROP_KEY }; // makes "C0" and "C1" available in the lobby
        EnterRoomParams enterRoomParams = new EnterRoomParams();
        enterRoomParams.RoomOptions = roomOptions;
        enterRoomParams.Lobby = sqlLobby;
        loadBalancingClient.OpCreateRoom(enterRoomParams);
    }

    private void JoinRandomRoom()
    {
        string sqlLobbyFilter = "C0 BETWEEN 345 AND 475 AND C1 = 'Map2'";
        //string sqlLobbyFilter = "C0 > 345 AND C0 < 475 AND (C1 = 'Map2' OR C1 = \"Map3\")";
        //string sqlLobbyFilter = "C0 >= 345 AND C0 <= 475 AND C1 IN ('Map1', 'Map2', 'Map3')";
        OpJoinRandomRoomParams opJoinRandomRoomParams = new OpJoinRandomRoomParams();
        opJoinRandomRoomParams.SqlLobbyFilter = sqlLobbyFilter;
        loadBalancingClient.OpJoinRandomRoom(opJoinRandomRoomParams);
    }

    // do not forget to register callbacks via loadBalancingClient.AddCallbackTarget
    // also deregister via loadBalancingClient.RemoveCallbackTarget
    #region IMatchmakingCallbacks

    void IMatchmakingCallbacks.OnJoinRandomFailed(short returnCode, string message)
    {
        CreateRoom();
    }

    void IMatchmakingCallbacks.OnCreateRoomFailed(short returnCode, string message)
    {
        Debug.LogErrorFormat("Room creation failed with error code {0} and error message {1}", returnCode, message);
    }

    void IMatchmakingCallbacks.OnJoinedRoom()
    {
        // joined a room successfully, both JoinRandomRoom or CreateRoom lead here on success
    }

    // [..] Other callbacks implementations are stripped out for brevity, they are empty in this case as not used.

    #endif
}

連鎖フィルター

1つの JoinRandomRoom操作で一度に最大3つのコンマ区切りのフィルターを送信できます。
これらは連鎖フィルターと呼ばれます。
Photonサーバーはフィルターを順番に使用しようとします。
いずれかのフィルターがルームと一致する場合、ルームに参加します。
それ以外の場合は、NoMatchFoundエラーがクライアントに返されます。

連鎖フィルターは、マッチメイキングのリクエストを保存し、そのプロセスをスピードアップするのに役立ちます。
これは、試行が失敗した後にフィルターを緩和する必要があるスキルベースのマッチメイキング で特に役立ちます。

可能なフィルター文字列形式:

  • 1(最小)フィルター値: {filter1}(または {filter1};
  • 2 フィルター値:{filter1};{filter2} (または {filter1};{filter2};)
  • 3 (最大) フィルター値: {filter1};{filter2};{filter3} (または {filter1};{filter2};{filter3};)

:

  • C0 BETWEEN 345 AND 475
  • C0 BETWEEN 345 AND 475;C0 BETWEEN 475 AND 575
  • C0 BETWEEN 345 AND 475;C0 BETWEEN 475 AND 575;C0 >= 575

カスタムルームリスト

クライアントは、SQLのようなクエリを使用して、SqlLobbyからルームのカスタムリストをリクエストすることもできます。
このメソッドは、条件に合致する最大100の部屋を返します。
返されたルームは参加可能で(つまり、空いていて満室ではない)、かつ表示されます。

C#

using Photon.Realtime;
using System.Collections.Generic;

public class GetCustomRoomListExample : ILobbyCallbacks
{
    private TypedLobby sqlLobby = new TypedLobby("customSqlLobby", LobbyType.SqlLobby);

    public void GetCustomRoomList(string sqlLobbyFilter)
    {
      loadBalancingClient.OpGetGameList(sqlLobby, sqlLobbyFilter);
    }

    // do not forget to register callbacks via loadBalancingClient.AddCallbackTarget
    // also deregister via loadBalancingClient.RemoveCallbackTarget
    #region ILobbyCallbacks

    void ILobbyCallbacks.OnRoomListUpdate(List<RoomInfo> roomList)
    {
        // here you get the response, empty list if no rooms found
    }

    // [..] Other callbacks implementations are stripped out for brevity, they are empty in this case as not used.

    #endif
}

スキルベースのマッチメイキング

SQLタイプのロビーを使用して、独自のスキルベースのマッチメイキングを実装できます。

まず、各ルームはプレイヤーがルームに参加するのに必要な固定されたスキルを取得します。
この値は変更してはいけません。変更されると、ルーム内のプレイヤーがそれまでに実施したマッチングがすべて無効になります。

通常、プレイヤーはJoinRandomRoomでルームに参加します。
フィルタはユーザーのスキルにもとづく必要があります。
クライアントは「skill +/- X」のルームに、簡単にフィルタリングを実行できます。

JoinRandomRoomは通常はすぐにレスポンスを取得しますが、もしマッチをすぐに見つけられない場合には、クライアントは数秒間待ってから再試行する必要があります。
リクエストの回数は任意に設定できます。
SQLロビータイプを使用する場合は、連鎖フィルターを使用できます。
何よりも、クライアントは時間の経過とともにフィルタールールを緩和することができます。

しばらくしてから、フィルタを緩めることは非常に重要です。
スキル面でそれほど適したプレイヤーではないが他に適したルームがなく、また他のプレイヤーとプレイしたほうがよい場合には、プレイヤーはそのルームに参加できます。

最大偏差とタイムアウトを定義します。
もしルームが見つからない場合、クライアントはこのユーザーが保有するスキルに適した新しいルームを作成する必要があります。
他のプレイヤーに対しても、同様の処理時間が必要となります。

当然のことながら、利用可能なルームの数が少ない場合、このワークフローには時間がかかります。
「アプリケーション統計」を参照し利用可能なルームがいくつあるかを確認することで、プレイヤーを助けることができます。
See Matchmaking For Low CCU.
「ルーム数が100未満」、「ルーム数が100以上、1,000未満」、「ルーム数が1,000以上」の場合などに分けて異なる設定をおこない、フィルタとタイミングを調整することが可能です。

除外されたSQLキーワード

SQLフィルターは、次のキーワードを受け入れません。

  • ALTER
  • CREATE
  • DELETE
  • DROP
  • EXEC
  • EXECUTE
  • INSERT
  • INSERT INTO
  • MERGE
  • SELECT
  • UPDATE
  • UNION
  • UNION ALL

SQLフィルター文字列でこれらの単語のいずれかを使用すると、対応する操作は失敗します。

非同期ランダムロビータイプ

このロビーはデフォルトのロビータイプに似ていますが、以下の2つの点が異なります:

  1. ルームエントリーはゲームサーバーから削除された後、1時間にわたってロビーリスト(マッチメイキングに利用可能)にとどまります。
    ルームは見える状態であり、また 非同期マッチメイキングで考慮されるようオープンになっている必要があります。
  2. ルームリストはクライアントに送信されません。

ロビータイプの比較

LobbyType 定期的なルームリストの更新 SQL Filter 最大プレーヤーフィルター カスタムルームプロパティフィルター マッチメイキングモード 削除されたルームのエントリTTL(分)
デフォルト 0
SQL 0
非同期 60

低CCUのマッチメイキング

充実したマッチメイキングには、ゲームに数百人のオンラインプレイヤーが必要です。
オンラインのプレイヤーが少ないと、価値のある対戦相手を見つけるのが難しくなります。どこかの時点で、ほとんどすべての、マッチを受け入れることが理想的です。

クライアント側でより精巧なマッチメイキングを構築する場合、これを考慮に入れる必要があります。
そのために、Photon Master Serverは接続されたユーザー、ルーム、およびプレイヤーの(ルーム内)の数を提供します。実行時にクライアント主導のマッチメイキングを調整できます。

ルームの数は、ゲームが現在どれだけ忙しいかを示す優れた指標になります。
ルームにいないプレイヤーの人数でマッチメイキングを微調整することもできます。
ルームにいないプレイヤーは参加しようとしているかもしれません。

たとえば、低CCUの状況を20室未満として定義できます。
したがって、ルームの数が20未満の場合、クライアントはフィルタリングを使用せず、代わりにQuick Matchルーチンを実行します。

開発の早い段階でのマッチメイキングのテスト

開発フェーズの早い段階でマッチメイキングをテストしていて、2つのクライアントからランダムルームにほぼ同時に参加しようとすると、両方のクライアントが異なるルームに参加する可能性があります。これは、ランダムルームに参加すると両方のマッチが返されないために発生します。 何も見つからなかったため、それぞれがおそらく新しいルームを作成します。
したがって、これは想定している動作であり、問題ではありません。
これを回避するにはJoinRandomRoomではなくJoinRandomOrCreateRoom (クイックマッチを参照してください)、その後にCreateRoomを使用してください。
そのほかに可能な回避策(開発目的のみ)は、ルームへの参加または再試行の前(または後)にランダムな遅延を追加することです。
また、アプリケーションまたはロビーの統計を聞いて、ルームが存在するか、作成されていることを確認することもできます。

その他のマッチメイキングオプション

独自のマッチメイキングを作成する場合には、マッチメイキングのほとんどがサーバー側で実行される点を確認してください。
サーバーからクライアントに送信されるルームのリストの更新頻度が低い(1〜2秒ごと)ため、ルームがどれくらい埋まっているかについてクライアントが得られる情報は不完全です。

プレイヤーが数千人いる場合には、複数のプレイヤーが同時に「参加」リクエストを送信します。
ルームがすぐに満員になると、プレイヤーがルームの参加に失敗する頻度が高くなり、マッチメイキングにかかる時間はより長くなります。

それに対して、サーバーは満員になりそうなルームを優先し、フィルタリングを考慮したうえでプレイヤーを完璧に配分できます。

Photonの外部でマッチメイキングを作成し(HTTPベースのWebサービスを介して)、Photonを使用してルームを作成して参加する(または「JoinOrCreate」を1回呼び出す)こともできます。
このようなマッチメイキングサービスは、(組み合わせて)Photonの「ネイティブHTTPモジュール」(カスタム認証/ WebRPC / WebHooks)またはカスタムプラグインを利用して、ルームの空室状況をWebサービスに報告できます。
マッチメイキング(キーワード)で考慮すべき事項:リージョン(および該当する場合はクラスター)、AppVersion、AppId、UserId、RoomName / GameId、Auth Cookie(カスタム認証)、URLタグ(WebHooks)など。

LoadBalancingサーバーアプリケーション、特にMasterServer、マッチメイキングの部分を変更する方法もあります。
このオプションはセルフホスティング専用です。

したがって、実際のゲームプレイが始まる前にルームを「ロビー」または「マッチメイキングの場所」として使用することは、ほとんどの場合、また、人気のあるゲームにはお勧めできません。

ロビーの制限

Photonには、以下のロビー関連のデフォルト制限があります。

  • アプリケーションあたりのロビーの最大数:10000
  • GameListイベント(デフォルトタイプでロビーに参加した際の初期リスト)のルームリストエントリの最大数:500
  • GameListUpdateイベント(デフォルトタイプでロビーに参加した際)でのアップデートされたルームエントリの最大数:500
    この制限は削除されたルームエントリ(表示されなくなった、または単に消えた部屋に対応する)は考慮していません
  • GetGameListオペレーションレスポンス(SQL Lobby)でのルームリストエントリの最大数:100

備考:

lobby v2では、GameListイベントまたはGameListUpdateイベントでデフォルトタイプの同一のロビーに各々参加したクライアントに送信される初期・更新後ルームエントリの数のみが500に制限されます。
GameListイベントでは、クライアントが受信する配列の長さが500を超えません。
GameListUpdateイベントでは、削除されたルームエントリ(ここでも送信されるものですが)の数には制限をかけていないためクライアントが受信する配列の長さが500を超えることもあります。制限されるのは更新済のルームエントリのみです。

Lobbies v2での新しい制限は、サーバーサイドでは何にも影響がありません。ルームはロビーに残ったままです。
制限をかけているのは、ロビーに参加したクライアントへブロードキャストするもののみです。これは帯域幅の理由によるもので、スペックに制限のあるクライアントに圧を掛けるためではありません。
500というのはかなり大きな数ですが、リストを全て見ようと下までスクロールするプレイヤーはいません。
また、定義上はロビーに長時間残っているクライアントには結果としてサーバー上の使用可能なルームの完全リストを手にする可能性があります。なぜなら、サーバーがキューにすべてのアップデートを追加し最大500の長さのバッチで送信するからです。
もちろん、ロビーにルームが存在してしばらく変わらない場合は、クライアントはそのルームを見逃してしまうこともあります。

サーバー上では、ロビーごとのルーム数に制限はありません。
FindFriends、CreateRoom、JoinRoom、JoinOrCreateRoomなどはLobbies v2への移行による影響を受けず、制限されません。これはクライアントがルームを無制限に作成できたり、ロビーリストの更新でクライアントに送信されていない部屋に参加したり、友達を見つけたりすることができます。

Back to top