This document is about: FUSION 1
SWITCH TO

Matchmaking API

はじめに

マルチプレイヤーゲームを作る上で重要なことは、同じスキルやレベルのプレイヤー、同じゲームタイプやマップをプレイしたいプレイヤーを簡単にマッチングさせ、ゲーム内の体験を出来るだけ楽しくすることです。 この目的のために、Photon Fusionは、完璧なマッチングを求めるプレイヤーに最高の体験を提供するためのAPIコールを公開しています。

Photon Fusionは、Photon Cloudと透過的に連動しています。Photonバックエンドサービスとのやりとりは、ほとんど内部で自動的に行われます。 このページでは、Fusion Matchmaking APIについて説明します。このAPIは、プレイヤーが希望するゲームプレイ体験に基づいて最適なSessionをフィルタリング/参加するために使用するカスタムプロパティを持つGame Sessionを作成/更新するために使用されます。

用語集

以下は、このドキュメントで使用されている、Matchmaking APIの正しい使い方に直接関係する用語の説明です。

  • Game Session: または単にSessionは、プレイヤーがマッチをプレイするために集まる場所、またはコミュニケーションをとる場所です。これはPhoton Cloudに公開され、他のクライアントが特定のマッチを検索、フィルタリング、参加できるようにするためのものです。セッションの外でのコミュニケーションは不可能で、どのクライアントも1つのルームでしか活動できません。ゲームセッションは次のようなプロパティとメソッドを持ちます: 名前による作成と参加、カスタムプロパティ、最大プレイヤー数、非表示(Lobbyに表示しない)または表示、閉じる(誰も入れない)または開くことが可能。
  • Lobby: Session の仮想的なコンテナまたは "リスト" 。複数のロビーを使って異なるゲームタイプでセッションを分割することも可能です。Fusionでは、ClientServer Mode (Server, Host, Client) で Session を作成すると、ClientServer という特定の Lobby に作成され、Shared Mode で作成すると、Shared という Lobby に作成されることになります。プレイヤーは Lobby 内で通信することはできず、他のクライアントがロビーにいることも知ることはできません。クライアントは、Lobby、Session、またはそのいずれかにのみ参加することができます。

ゲームセッションの作成と参加

Game Session の作成と参加は同じ手続きの2部構成になっており、そのルールは単純です。 1. 指定した SessionName を持つ Session が存在しない場合、その SessionName で新しいセッションを作成します (以下に説明するように、すべての場合ではありません) 2. ピアがその SessionName を持つセッションに参加する。

APIに関しては、新しいFusion Simulationが開始されると、これら全てが自動的に行われます。新規にセッションを作成する際に、セッションをカスタマイズするために使用できる主な引数は、以下のとおりです。

C#

NetworkRunner.StartGame(new StartGameArgs {
  // other args...
  SessionName = [string]
  SessionProperties = [Dictionary<string, SessionProperty>],
  CustomLobbyName = [string],
  PlayerCount = [int],
  DisableNATPunchthrough = [bool],
  CustomSTUNServer = [string],
  AuthValues = [AuthenticationValues],
  DisableClientSessionCreation = [bool]
});

Game Sessionに関連するすべての引数は任意であり、それぞれのデフォルト値は以下のとおりです。

  • SessionName: Game Sessionの名前です。Photon Cloud上でセッションを識別し、Region内で一意である必要があります。 デフォルトでは、Nameが設定されていない場合、Fusionはセッションを識別するためにランダムなGUIDを生成します。2つのケースが考えられます。
  1. 特定の SessionName で開始すると、Fusion は 常に その Name を持つ Session を作成/参加させようとします。これはFusionの重要な要素です。クライアントピアはセッションを作成することができ、(これをlate-join-serverと呼びます)サーバはクライアントが作成したセッションに事前に参加することができます。

  2. SessionName を指定しない場合、Fusion は Host, Server, Shared ゲームモードで起動した場合のみ、新しいセッションを作成/参加し、Client モードで起動した場合はランダムな Session に参加しようと試みます。

  • SessionProperties: Session の Custom Properties は、ゲームモード/タイプや現在読み込まれているマップなど、 Game Session に関するメタデータを含める方法です。このプロパティは Session を作成する際に、常に Session Lobby に公開されることを覚えておいてください。この引数のもう一つの使い方は、Client がランダムな Session に参加する際のマッチングフィルターとして使用します。トラフィックを最小限にするために、常に Properties Keys の文字列をできるだけ短く保つようにしてください。デフォルトでは、Session Custom Properties は空で、余分な情報は含まれていません。
  • CustomLobbyName: Lobby とは、似たような Game Sessions を集約するためのもので、例えば異なるゲームタイプのセッションを分離するために使用することができます。この引数で、Sessionが関連付けられるカスタムLobby Nameを設定します。デフォルトでは、Fusion は GameMode に基づいて Session を分離します。Session が Host, Server, Client ゲームモードで作成された場合は ClientServer Lobby で、Shared ゲームモードで作成された場合は Shared Lobby で分離されます。
  • PlayerCount: この Session に接続できるクライアントの最大人数を定義します。このパラメータは新しい Session を作成するときのみ使用され、デフォルトでは NetworkProjectConfig/Simulation の Default Players 設定で設定された値が使用されます。
  • DisableNATPunchthrough: Photon Fusionに実装されているNATパンチスルーシステムを無効にするためのブーリアンフラグです。これにより、クライアントとサーバ間で中継された接続が行われるようになります。ゲームサーバにtrueを設定すると、全てのクライアントがリレー接続され、Clientに設定すると、特定のピアだけがリレー接続されます。
  • CustomSTUNServer: ピアのリフレクティブアドレスを解決するために使用するカスタムSTUNサーバーを指定します。
  • AuthValues: 外部サービスを使用してピアを認証するために使用されるカスタム認証値です。認証は、Photon Dashboard で特定の Application ID に対して事前に設定されたサービスを使用して行われます。
  • DisableClientSessionCreation: Client として起動するピアが、 SessionName を指定しても新しい Session を作成しないようにするためのブール型フラグです。デフォルトでは、クライアントは Session を作成し、 Server が参加するのを待つことができますが、このフラグを使用すると、この動作を無効にすることができます。

このAPIでは、ランダムに、あるいは特定の SessionName で Game Session を作成して参加することができます。これは、たとえば Game Invitation を受け取ったときに使用することができますが、カスタムプロパティを使用して Session をフィルタリングし、特定の構成のゲームにのみ参加することもできます。 これにより、セッションを管理する際の柔軟性が格段に向上します。

以下の表は、シミュレーション開始時の SessionName と GameMode に依存する Game Session の作成と参加について、Fusionがどのように処理するかをまとめたものです。

有効なセッション名 ゲームモード 動作
はい ホスト/サーバー セッションを作成する
クライアント セッションに参加する、見つからない場合は失敗する
共有セッションに参加する、見つからない場合は作成する
いいえ ホスト/サーバー ランダムな名前でセッションを作成する
クライアント ランダムなセッションに参加する、見つからなければ失敗
共有 ランダムなセッションに参加する、見つからない場合はランダムな名前でセッションを作成する

ゲームセッション情報の取得と更新

Fusion では、現在接続している Game Session の Name や Region など、様々な情報を提供しています。 これらのデータは、NetworkRunnerの SessionInfo プロパティから直接取得することができます。 以下に、SessionInfo タイプで利用可能な全ての欄を示します。

  • IsValid [bool{get}]: SessionInfo が読み込み/書き込みの準備ができているかどうかを示す。
  • Name [string{get}]: セッション名
  • Region [string{get}]: 現在接続している Region
  • Properties [Dictionary<string, SessionProperty>{get}]: 現在の Session Custom Properties を含む読み込み専用の辞書。これらのプロパティを更新するには、 SessionInfo.UpdateCustomProperties(Dictionary<string, SessionProperty>) メソッドを使用して、新しいプロパティセットを渡してください。
  • IsVisible [bool{get,set}]: Session が Lobby 上で Visible であるかどうかのシグナルを返します。セッションを不可視にするには、このプロパティを変更するだけです。
  • IsOpen [bool{get,set}]: Session が Open で参加できるかどうかのシグナルです。セッションを閉じたり開いたりするには、このプロパティを変更するだけでよい。
  • PlayerCount [int{get}]: Session にいる現在の Players の数です。ロビーでのみ利用可能です。
  • MaxPlayers [int{get}]: セッション に参加できるピアの最大数です。この値には サーバー/ホスト ピアのスロットも含まれます。*ロビーでのみ利用可能です。

Session情報と、主に Custom Properties は、Matchmaking の目的のみに使用し、ゲームクライアントとゲームの状態を同期させるtためには **決して ** 使用しないでください。そのような使い方は非常にお勧めできません。 ゲームプレイに関連する情報をセッション全体で交換する必要がある場合、Fusionには、グローバルなNetworkObjectを用意したり、ワンショットのデータにはRPCを使用するなど、様々なオプションが用意されています。

また、NetworkRunnerは、ゲーム内で使用できる Session や Photon Cloud 関連のプロパティもいくつか提供しています。例えば:

  • NetworkRunner.IsCloudReady: ローカルピアが Photon Cloud に接続し、ルームの作成/参加やロビーへの参加が可能かどうかのシグナル。
  • NetworkRunner.UserId: 認証されたローカルピアの UserId を保持。この情報は、アプリケーションで使用される認証サービスから取得されます。
  • NetworkRunner.AuthenticationValues: Fusionの起動時にローカルピアの認証に使用された AuthenticationValues のリファレンスを保持。
  • NetworkRunner.CurrentConnectionType: ピアが現在使用している Connection Type を記述します。リモートの Server との接続は Direct または Relayed です。SharedMode では、クライアントは常にリレー経由で接続することに留意してください。
  • NetworkRunner.NATType: NAT Punchthrough Systemが有効な場合、Fusionはローカルピアが動作しているネットワークの現在のNAT Typeを判断しようとします。NAT タイプは、Invalid、UdpBlocked、OpenInternet、FullCone または Symmetric です。
  • NetworkRunner.IsSharedModeMasterClient: ローカルピアが Shared Game Session の Master Client でもあるかどうかを表すブーリアンフラグです。これは SharedMode で動作しているときのみ有効で、他のクライアントと Master Client の区別に基づいて、どのピアで特定のアクションを発生させるかを決定するために使用することができます。

ロビーからゲームセッションに参加する

正しいGame Sessionを見つけるもう一つの方法は、Sessionsのリストを提供し、プレイヤーが参加するセッションを選択できるようにすることです。 この場合、ロビーに参加するのが良い方法ですが、必要でない場合はこの方法を使わないことをお勧めします。 ほとんどのゲームタイプでは、プロパティフィルタに基づいたSessionへの参加が最適ですが、FusionではSession Listingも簡単に行うことができます。

通常のフローを使い、上記のようにFusionを起動するのではなく、Session Listingは少し違ったフローをたどります。

  1. Join a Lobby: Fusion Runner のリファレンスを使い、NetworkRunner.JoinSessionLobby(SessionLobby, [string]) をコールするだけで、ローカルピアが Photon Cloud に接続し、特定の Lobby に入室できるようにします。このメソッドは2つの引数を受け取ります。
  • SessionLobby: 以下の値のいずれかを指定することができます。
    1. ClientServer で、デフォルトの ClientServer Lobby に参加。 2.Shared はデフォルトの Shared Lobby に参加。
    2. Custom は、Custom Lobby Name と共に使用。
    • LobbyName: これは以前のGame Sessionを作成するときに使用した Custom Lobby Name であるべきです。
  1. ゲームセッションのリストを取得する: Fusion を使う場合、主な API エントリポイントの一つは INetworkRunnerCallbacks で、これは Fusion が Lobby からのセッションリストを含む一連の異なるイベントを表示するために使用する特別なインターフェースです。OnSessionListUpdated(NetworkRunner runner, List<SessionInfo> sessionList) コールバックは、セッションの作成/削除やセッションのプロパティが更新されるなど、セッションのリストが変更されるたびに呼び出されます。SessionInfoは上記と同じタイプです。その後、リストの表示、フィルタリング、順序付けなどを行うことができます。
  2. Join a Session: 参加する Session を選択すると、通常の NetworkRunner.StartGame() を使って Fusion を起動することができますが、この場合、クライアントの起動に使う SessionName はセッションのものでなければなりません。そうすれば、Clientはこの特定のGame Sessionに参加することになります。
    • 通常通り、正しい GameMode を選択します。ピアは Session に参加するので、 GameMode.Client または GameMode.Shared モードのどちらかでなければなりません。
  • SessionName 欄には SessionInfo.Name を設定する必要があります。これは Game Session の識別子となるためです。
  • 他のすべてのパラメータは任意で、例えば SceneObjectProvider のように、適宜初期化する必要があります。

APIの使用例

ランダムセッションにジョインする

セッションにジョインするためには(プレイヤーがゲームにさっとジョインする場合)、NetworkRunnerを開始して、パラメータを加えずに利用可能なGame Sessionを探します。

C#

public async Task StartPlayer(NetworkRunner runner) {
  var result = await runner.StartGame(new StartGameArgs() {
    GameMode = GameMode.AutoHostOrClient, // or GameMode.Shared
  });

  if (result.Ok) {
    // all good
  } else {
    Debug.LogError($"Failed to Start: {result.ShutdownReason}");
  }
}

こうすることで、ローカルピアがランダムなGame Sessionを開始して接続できるようになります。何も見つからない場合は、ランダムなSession Nameで新しいセッションが作成されます(GameMode.AutoHostOrClientを使用しているため)。Sharedモードのセッションでも、GameMode.Sharedを使用してNetworkRunnerを起動していた場合、こちらは有効です。

カスタムプロパティで新しいゲームセッションを開始する

この例では、Hostがいくつかのカスタムプロパティを持つ Game Session を作成します。後で Clients がこれらのプロパティを使用して Sessions をフィルタリングすることができます。

C#

// Some predefined types used as values for the Game Session Properties
public enum GameType : int {
  FreeForAll,
  Team,
  Timed
}

public enum GameMap : int {
  Forest,
  City,
  Desert
}

// Utility method to start a Host using a defined GameMap and GameType
public async Task StartHost(NetworkRunner runner, GameMap gameMap, GameType gameType) {

  var customProps = new Dictionary<string, SessionProperty>();

  customProps["map"] = (int)gameMap;
  customProps["type"] = (int)gameType;

  var result = await runner.StartGame(new StartGameArgs() {
    GameMode = GameMode.Host,
    SessionProperties = customProps,
  });

  if (result.Ok) {
    // all good
  } else {
    Debug.LogError($"Failed to Start: {result.ShutdownReason}");
  }
}

サンプルコードでは Game Session の Custom Properties の値に Enums を使用していますが、これは値に意味を持たせるための一つの方法に過ぎません。 Runner.StartGame をHostとして呼び出す (GameMode = GameMode.Host) だけで、Random Name (SessionName 引数が渡されなかったため) で新しいセッションが開始され、SessionProperties 引数を使用すると、Fusion は Session にそれらのプロパティを含めることができます。

フィルタを使ってランダムセッションに参加する

上記のサンプルコードから、特定のGameTypeを持つ、任意のGameMap上の任意のゲームセッションに参加するClientを起動する方法を説明します。 スタートアップのコードは基本的に同じで、GameModeが GameMode.Client に設定され、customPropsには type キーと希望の gameType 値のみが含まれています。

C#

public async Task StartClient(NetworkRunner runner, GameType gameType) {

  var customProps = new Dictionary<string, SessionProperty>() {
    { "type", (int)gameType }
  };

  var result = await runner.StartGame(new StartGameArgs() {
    GameMode = GameMode.Client,
    SessionProperties = customProps,
  });

  if (result.Ok) {
    // all good
  } else {
    Debug.LogError($"Failed to Start: {result.ShutdownReason}");
  }
}

これだけで、クライアントはその特定の GameType のランダムな Session に参加できるようになります。

ロビーからセッションに参加する

Fusionをすぐに起動するのではなく、LobbyからSessionを取得するには、別のメソッドセットが必要です。 以下のサンプルコードでは、Fusion Runner が Photon Cloud に接続し、あらかじめ定義された ClientServer ロビーに参加するようにしています。

C#

// Utility method to Join the ClientServer Lobby
public async Task JoinLobby(NetworkRunner runner) {

  var result = await runner.JoinSessionLobby(SessionLobby.ClientServer);

  if (result.Ok) {
    // all good
  } else {
    Debug.LogError($"Failed to Start: {result.ShutdownReason}");
  }
}

また、前述したように、この方法で ClientServer, Shared, Custom ロビーに Client が参加することが可能です。 以下のように、サーバ/ホストが Custom ロビーで Session を作成する場合です。

C#

public async Task StartHost(NetworkRunner runner) {

  var result = await runner.StartGame(new StartGameArgs() {
    GameMode = GameMode.Host,
    CustomLobbyName = "MyCustomLobby"
  });

  if (result.Ok) {
    // all good
  } else {
    Debug.LogError($"Failed to Start: {result.ShutdownReason}");
  }
}

Client の Join Lobby リクエストを変更する必要があります。

C#

// Utility method to Join a Custom Lobby
public async Task JoinLobby(NetworkRunner runner) {

  var result = await runner.JoinSessionLobby(SessionLobby.Custom, "MyCustomLobby");

  if (result.Ok) {
    // all good
  } else {
    Debug.LogError($"Failed to Start: {result.ShutdownReason}");
  }
}

接続が確立すると、Fusion Runner は登録された全ての INetworkRunnerCallbacks に対して OnSessionListUpdated コールバックを呼び出します。 以下に、リストの最初の Session に参加する方法を示すサンプルコードがあります。

C#

public class MyBehaviour : Fusion.Behaviour, INetworkRunnerCallbacks {

  // other callbacks...

  // Receive the List of Sessions from the current Lobby
  public void OnSessionListUpdated(NetworkRunner runner, List<SessionInfo> sessionList) {

    Debug.Log($"Session List Updated with {sessionList.Count} session(s)");

    foreach (var session in sessionList) {

      Debug.Log($"Joining {session.Name}");

      // This call will make Fusion join the first session as a Client
      runner.StartGame(new StartGameArgs() {
        GameMode = GameMode.Client, // Client GameMode
        SessionName = session.Name, // Session to Join
        SceneObjectProvider = GetSceneProvider(runner), // Scene Provider
        DisableClientSessionCreation = true, // Make sure the client will never create a Session
      });

      return;
    }
  }
}
Back to top