マッチメイキングガイド

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

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

目次

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

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

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

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

このワークフローでは、プレイヤーは長いルームリストから(ランダムに)ルームを選択することなくルームに入室されます。 プレーヤーをルームに素早く入室させるには、以下の手順を実行します。

  • 「ランダムに参加する」を試してください。API/プラットフォームによって異なりますが、これはOpJoinRandomまたはJoinRandomRoomという名前のオペレーション です。

    • 正常な場合にはクライアントは問題なくルームに参加します。
    • 問題がある場合にはルームが存在しないか、またはどのルームにも空きがない、という状態になります。

    • すぐにルームが見つからない場合には、ルームを作成します。

  • ルームの名前を表示しない場合は名前を作成しないでください。サーバーが自動で設定します。OpCreatRoomを呼ぶ際に「ルーム名」にnullか「」を設定します。ルームは一意のGUIDを取得します。
  • 最大プレイヤー数」に値を適用してください。これによってサーバーはプレイヤーの追加を停止します。
    • クライアントがルームに1人だけで存在している(players ==1)場合:待機してください。対戦相手を待っていることを示す画面を表示します。
    • ルームの中に充分なプレイヤーが存在する場合、「スタート」からゲームを開始できます。新しいプレイヤーを追加しない場合、ルームを「クローズ」から閉めてください。満員でない場合でも、サーバーがルームへの増員を停止します。 -注意:ルームを閉める際、既にその瞬間にルームに参加しようとしていたプレイヤーを入れるための待機時間が発生します。そのため、ルームを閉めた後でもプレイヤーが参加する場合があります。

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

この方法を使用する場合、クライアントAPIの「Auto-Join Lobby」の設定を確認してください(「autoJoinLobby」という名前のパラメータまたは、それに類似したパラメータ)。 クライアント参加のワークフローを高速化するため、上記がfalseに設定されていることを確認してください。

それほどランダムではないマッチメイキング

完全なランダムマッチメイキングをプレイヤーが常に好むとは限りません。 特定のマップやモードでのプレイが好まれる場合もあります(2対2など)。

Photon CloudとLoadbalancingでは、任意の「カスタムルームプロパティ」を設定でき、これらのプロパティはJoinRandomのフィルタとして使用可能です。

ルームプロパティとロビー

ルームのプロパティはルーム内のすべてのプレイヤーと同期しており、現在のマップやラウンド、開始時間のトラッキングに使用されます。

これらは文字列キーを含むHashtableとして扱われます。 短い名前の方が良いので、たとえば「GameMode」ではなく「gm」などを用いるとよいでしょう。

デフォルトでは、簡潔化のためプロパティはマスターサーバーに送信されません。「map」や「game mode」をマッチングで利用できるようにするには、ルームの作成時に「ロビー内で表示されるルームプロパティ」のリストを設定してください。

RoomOptions roomOptions = new RoomOptions();
roomOptions.CustomRoomPropertiesForLobby = { "map", "ai" };
roomOptions.CustomRoomProperties = new Hashtable() { { "map", 1 } };
roomOptions.MaxPlayers = expectedMaxPlayers;
lbClient.OpCreateRoom(roomName, roomOptions, typedLobby);

デフォルトでは「ai」には値がありません。Room.SetCustomPropertiesによってルーム内に設定されるまで、「ai」はロビーに表示されません。 「map」または「ai」の値を変更すると、これらは少しの時間が経過してからロビー内でも更新されます。

リストの読み込みによってクライアントのパフォーマンスが低下しないよう、リストは短くしてください。

繰り返しになりますが、roomPropsInLobbyを使用するためにロビーに参加する(また、非常に長いルームリストを取得する)必要はありません。 ルームプロパティをロビーに設定すると、これらのプロパティはフィルタでも使用可能となります。

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

OpJoinRandomでは、予測されるルームプロパティと最大プレイヤー数の値とともにHashtableを渡すことができます。 これらは、サーバーが「適切」なルームを選択する際のフィルタとして機能します。

Hashtable expectedCustomRoomProperties = new Hashtable() { { "map", 1 } };
lbClient.OpJoinRandomRoom(expectedCustomRoomProperties, expectedMaxPlayers);

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

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

低いCCU向けのマッチメイキング

最適な(スキルベースの)マッチメイキングでは、ゲームでは数百組のプレイヤーがオンラインである必要があります。プレイヤー数が少なくなるにつれて、ふさわしい対戦相手を見つけることが難しくなり、あらゆるマッチングを受け入れる必要が生じます。

より複雑なマッチメイキングをクライアント側で構築する場合には、この点を考慮しなければなりません。これを実現するために、Photonマスターサーバーは接続ユーザー数、ルーム数、プレイヤー数(同一ルーム内)を提供します。このため、実行時にはクライアント主導のマッチメイキングを調整できます。

LoadBalancingClient.RoomsCount は、そのゲームの現在のビジー状態を示す包括的な指標です。また、何人のプレイヤーが1つのルーム内にいないか、によってマッチメイキングを微調整することもできます(LoadBalancingClient.PlayersOnMasterCount)。 ルーム内にいないプレイヤーが対戦相手を探している可能性もあります。 たとえば、20ルーム未満などのCCUの低い状態を定義できます。LoadBalancingClient.RoomsCount が20未満の場合には、クライアントはフィルタリングを使用する代わりにランダムマッチメイキングルーチン(ルームの検索を試行し、失敗した場合にはルームを作成)を実行します。

マッチングに時間がかかる点をプレイヤーに伝えたい場合には、想定よりも長い待ち時間を表示することができます。 当然のことながら、スキルベースのマッチメイキングに2つの層が発生します。

友達と一緒にプレイ

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

たとえば「friendName1 + friendName2 + randomInteger」のように、一意のルーム名が作成されます。 他のプレイヤーをルームに入れたくない場合は、見えないルームとしてルームを作成してください:

RoomOptions roomOptions = new RoomOptions();
roomOptions.IsVisible = false;
lbClient.JoinOrCreateRoom(nameEveryFriendKnows, roomOptions, typedLobby);

JoinOrCreateRoomによって、そのルームがまだ存在しない場合にはルームが作成されます。 ルームが満員の場合、OpJoinが失敗します。OnOperationResponseを確認してください。

OpFindFriends で友達を見つけることもできます。これを実行するには、一意のユーザーIDが必要です。

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

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

備考:

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

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

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

// join room example
lbClient.OpJoinRoom(roomName, expectedUsers);
// create room example
lbClient.OpJoinOrCreateRoom(roomName, roomOptions, typedLobby, expectedUsers);
// join random room example
lbClient.OpJoinRandomRoom(expectedProperties, maxPlayers, expectedUsers, matchmakingType, typedLobby, sqlLobbyFilter, expectedUsers);
// create room example
lbClient.OpCreateRoom(roomName, roomOptions, typedLobby, expectedUsers);

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

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

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

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

lbClient.OpJoinRandomRoom(expectedProperties, maxPlayers, expectedUsers, matchmakingType, typedLobby, sqlLobbyFilter, teamMembersUserIds);

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

lbClient.OpCreateRoom(roomName, roomOptions, typedLobby, teamMembersUserIds);

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

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

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

lbClient.OpJoinRoom(roomNameWhereTheLeaderIs);

ロビー

Photonは、「ロビー」でルームを管理します。 デフォルトのロビーはありますが、クライアントはすぐにロビーを新規作成できます。 ロビーは、CreateRoomでロビーを指定した時点から存在し始めます。

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

現在ではロビーへの参加は推奨していません: 多くの場合、クライアントは長いルーム名のリストを取得し、プレイヤーはランダムにルームを選んで、やっとプレイを開始します。 すべて同じPINGを持つ長いルーム名のリストに、多くの情報は含まれていません。

プレイヤーがマッチメイキングをより細かく管理できるようにするには、ランダムマッチメイキング用のフィルタを使用します。

(サーバー側の)ランダムマッチメイキングでも使用されるので、複数のロビーが便利な場合もあります。

ロビーは、名前とタイプによって識別されます。 名前はどのような文字列でも可能ですが、ロビーのタイプには3種類しかありません。 それぞれが固有な特性で、特定の用途に用いられます。

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

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

たとえば、推奨されるデフォルトのロビー(TypedLobby.Default)の名前はnullでタイプはLobbyType.Defaultです。

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

SQLロビータイプ

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

  • デフォルトの「予期されるロビープロパティ」フィルタリングは、JoinRandomRoom内の SQLフィルタリングに切り替えられました。
  • カスタムルームリスティング

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

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

例:

RoomOptions roomOptions = new RoomOptions();
roomOptions.MaxPlayers = expectedMaxPlayers;
// in this example, C0 might be 0 or 1 for the two (fictional) game modes
roomOptions.CustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "C0", 1 } };
roomOptions.CustomRoomPropertiesForLobby = new string[] { "C0" }; // this makes "C0" available in the lobby
// let's create this room in SqlLobby "myLobby" explicitly
TypedLobby sqlLobby = new TypedLobby("myLobby", LobbyType.SqlLobby);
lbClient.OpCreateRoom(roomName, roomOptions, sqlLobby);

クエリはJoinRandomGameオペレーション内で送信可能です。 フィルタリングクエリは基本的にSQLで、条件文は「C0」から「C9」までの値にもとづきます。 SQLiteがサポートするすべての演算子のリストと、それらの使い方についてはこちらを参照してください。

TypedLobby sqlLobby = new TypedLobby("myLobby", LobbyType.SqlLobby);    // same as above
string sqlLobbyFilter = "C0 = 0";   // find a game with mode 0
lbClient.OpJoinRandomRoom(null, expectedMaxPlayers, matchmakingMode, sqlLobby, sqlLobbyFilter);
// other filter examples:
// "C0 = 1 OR C6 > 50"
// "C5 = \"Map2\" AND C2 > 10 AND C3 < 20"
// "C8 BETWEEN 0 AND 100"
// "C4 IN ('Map1', 'Map2', 'Map3')"

カスタムルームリスティング

クライアントは、SQLに似たクエリを使用してSqlLobbyからルームのカスタムリストを要求できます。

TypedLobby sqlLobby = new TypedLobby("myLobby", LobbyType.SqlLobby);    // same as above
string sqlLobbyFilter = "C0 = 0";   // request games with mode 0
lbClient.OpGetGameList(sqlLobby, sqlLobbyFilter);

このオペレーションは非同期です。 レスポンスには、もし存在するならば返されたルームリストが含まれます。

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

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

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

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

JoinRandomRoomは通常はすぐにレスポンスを取得しますが、もしマッチをすぐに見つけられない場合には、クライアントは数秒間待ってから再試行する必要があります。 リクエストの回数は任意に設定できます。クライアントはフィルタルールを徐々に緩められます。

しばらくしてから、フィルタを緩めることは非常に重要です。

スキル面でそれほど適したプレイヤーではないが他に適したルームがなく、また他のプレイヤーとプレイしたほうがよい場合には、プレイヤーはそのルームに参加できます。

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

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

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

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

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

このため、このタイプではロビーへの参加は意味を成しません。

このタイプのロビーでは非同期(再)参加を十分に機能させるため、webhooksかもしくはルームステートを持続させるための他の方法と組み合わせる必要があります。

ロビータイプの比較

ロビータイプ ルームリストの定期的なアップデート SQLフィルタリング カスタムルームプロパティのフィルタリング 削除済みのルームエントリーのTTL(分)
デフォルト 0
SQL 0
非同期 60

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

独自のマッチメイキングを作成する場合には、マッチメイキングのほとんどがサーバー側で実行される点を確認してください。 サーバーからクライアントに送信されるルームのリストの更新頻度が低い(1〜2秒ごと)ため、ルームがどれくらい埋まっているかについてクライアントが得られる情報は不完全です。 プレイヤーが数千人いる場合には、複数のプレイヤーが同時に「参加」リクエストを送信します。 ルームがすぐに満員になると、プレイヤーがルームの参加に失敗する頻度が高くなり、マッチメイキングにかかる時間はより長くなります。

それに対して、サーバーは満員になりそうなルームを優先し、フィルタリングを考慮したうえでプレイヤーを完璧に配分できます。 つまり、実際のゲームプレイが開始する前にルームをマッチングの場所として使用することは、人気が高いゲームの場合にはあまり良い案とはいえません。

 ドキュメントのトップへ戻る