はじめに
オンラインマルチプレイゲームを作る場合、プレイヤー同士のマッチングシステムを用意したい場合がほとんどだと思います。
Photon Fusionには、そのためにMatchmakingAPIというものが用意されています。
本記事では過去記事で紹介した公式サンプルを元に、MatchmakingAPIの基礎的な内容を紹介します。
前提記事
Photon Fusionの基本的な解説は以下の記事で行っていますので、そちらを参照下さい。
動作確認環境
Windows 11 Home 22H2
Unity 2022.3.2f1
Fusion SDK 1.1.8 F Build 725
MatchmakingAPIとは
プレイヤー同士をマッチングさせるための情報のやりとりや、同一セッションに接続しゲームを開始するための機能で、以下の3つが主に使われます。
-
NetworkRunner.StartGame
基本的なメソッド
プレイヤー同士の情報が通信できるセッションを作成、接続する
-
NetworkRunner.JoinSessionLobby
セッションを検索するためのロビーを作成、接続する
-
SessionInfo.UpdateCustomProperties
参加セッションの情報を更新する
また重要な用語として以下が挙げられます。
-
Game Session
ゲームを行うための部屋のようなもの
複数作成可能
Lobbyの中に存在し、同Lobby内のsessionの情報を取得可能
-
Lobby
セッションに入る前の状態
複数作成可能
Lobby内に作られている全てのsessionの情報を取得可能
他のプレイヤーの情報は取得できない
以下の画像は上記のメソッドや用語がどのような関係にあるかを表した図です。
MatchmakingAPIの使い方
先の画像からもわかるように、処理の流れは以下のようになっています
ロビーに接続する
まずはNetworkRunner.JoinSessionLobbyメソッドを使いロビーに接続します。ランダムマッチやフレンドからの招待など、セッションを探す必要がなければこの工程は飛ばして構いません。
//オリジナルのスクリプト
//INetworkRunnerCallbacksを継承
private void Awake()
{
_runner = GetComponent<NetworkRunner>();
Connect();
}
private async void Connect()
{
_runner.AddCallbacks(this);
var result = await _runner.JoinSessionLobby(SessionLobby.Shared,"MyCustomLobby");
if (result.Ok) {
// all good
Debug.Log("JoinSessionLobby succeeded");
} else {
Debug.LogError($"Failed to Start: {result.ShutdownReason}");
}
}
ロビーに接続したあとは目的のセッションを探し、次の工程へ進みます。
セッション情報はロビーにいる限り、セッション情報が更新されるたびにOnSessionListUpdatedコールバックで取得する事ができます。実装については後述します。
セッションに接続
接続するセッションを特定できたらNetworkRunner.StartGameメソッドでセッションへ参加します。GameMode、セッション名の有無、セッションの有無、これらの組み合わせで接続の挙動が変わってくるため、詳細は公式マニュアルを参照ください。
StartGameの主な引数は以下のような意味を持ちます。
-
GameMode
ネットワーク・トポロジーを選択する
-
SessionName
セッションに名前を設定する
セッション名はRegion内でユニークな値
-
SessionProperties
セッションに関するメタデータ
Session Lobbyから常に見れる
-
CustomLobbyName
Lobbyに名前をつける
他のLobbyの情報は見えない
セッション・ロビーから抜ける
ゲームを終えた場合や再マッチングを行う場合など、セッションやロビーから抜けたい場面では以下の方法が使えます。
-
別のセッションへ行く
NetworkRunner.StartGame
-
ロビーに戻る
NetworkRunner.JoinSessionLobby
-
ローカルに戻る
NetworkRunner.Shutdown
MatchmakingAPIの実装例
マッチングにまつわる機能には、セッション検索やランダムマッチ、プライベートマッチなど色々なものがあります。ここでは代表的な機能を紹介します。
なお、コードの一部は公式サンプルのSocialHubを利用しています。
なおSocialHubの活用&改造例は過去記事を参考にできます。
ランダムマッチ
公式サンプルではこの方式を多く用いています。
StartGameメソッドにおいて、セッション名を指定しなければ同名ロビー内のランダムなセッションに接続します。
await runner.StartGame(new StartGameArgs() {
GameMode = GameMode.AutoHostOrClient, // or GameMode.Shared
});
セッション情報の追加
遊び方や目的、対戦レギュレーションなどが複数ある場合、適切にマッチングを行うためにその情報をセッションが保持している必要が出てきます。
SessionPropertyはそのためのオプションで、情報を設定するには2つ方法があります。
- StartGame時にSessionPropertyに追加する
- セッション内でSessionInfo.UpdateCustomPropertiesメソッドで変更する
この情報を使うことで、マッチング機能を拡張したり、ランダムマッチに応用することもできます。
サンプルでは、各情報は事前にConnectionDataを経由し他の場所で設定しています。
//公式サンプルSocialHub
public async Task ConnectToRunner(ConnectionData connectionData, Action<NetworkRunner> onInitialized = default, Action<ShutdownReason> onFailed = default)
{
//略
var sessionProperties = new Dictionary<string, SessionProperty>()
{ { "ID", (int)connectionData.ID },
{"Target", connectionData.Target.ToString() },
{"Rate", connectionData.Rate.ToString() },
};
//略
var startResult = await connection.Runner.StartGame(new StartGameArgs()
{
GameMode = gameMode,
SessionProperties = sessionProperties,
DisableClientSessionCreation = false,
Scene = scene,
PlayerCount = connectionData.MaxClients,
Initialized = onInitialized,
SceneManager = sceneManager,
ObjectPool = connection.Runner.GetComponent<INetworkObjectPool>(),
});
}
セッション一覧の表示や検索
ロビーに参加することでOnSessionListUpdatedコールバックによりセッションリストを取得することができます。このコールバックは、同一ロビー内に存在するセッションが更新される度に受け取ります。
リストには設定したセッション情報のSessionNameやSessionPropertiesが含まれているため、セッションの一覧を表示したりフィルタしたり、セッションに参加することができます。
//オリジナルのスクリプト
//INetworkRunnerCallbacksを継承
public void OnSessionListUpdated(NetworkRunner runner, List<SessionInfo> sessionList)
{
//前略
//初期化やフィルタリングを行う
//リストデータをUIオブジェクトに流す
for (int i = 0; i < sessionList.Count; i++)
{
var session = sessionList[i];
roomDataList[i].SetData(session.Properties["ID"].PropertyValue.ToString()
,session.PlayerCount + "/" + session.MaxPlayers
,session.Name);
roomDataList[i].GetComponent<Button>().onClick.RemoveAllListeners();
roomDataList[i].GetComponent<Button>().onClick.AddListener(()=>Launch(session.Name));
roomDataList[i].SetActive(true);
}
}
なおこの機能を実装するにあたって注意することが2つあります。
1つ目は、ロビーに入っている状態もCCU(concurrent user、同時接続数)にカウントされてしまうことです。セッションに参加した状態でNetworkRunnerを生成しロビーにも参加することができますが、この場合一人で2CCUとして計算されます。
無料プランでは20CCU、ひとつ上のプランでも100CCUの制限があるため、適宜接続を切る必要があります。
2つ目はセッション情報のトラフィックです。ロビーにいる間、セッション情報に変更があるたびにOnSessionListUpdatedが呼ばれるため、ユーザー(セッション数、ロビーの人数)が増えるとトラフィックが大きくなってしまいます。そのためSessionPropertyはなるべく小さくなるようにしたほうが良いでしょう。
また、そもそもマッチングすることのない母集団、例えばプライベートマッチをしたい人、ランクマッチをしたい人、特殊ルールでランダムマッチしたい人がいた場合、予めCustomLobbyNameによって分けておく方が良いかもしれません。