Matchmaking API
簡介
在創建多人遊戲時,一個關鍵的要求是能夠很容易地將具有類似技能、水平或想玩相同遊戲類型或地圖的玩家匹配在一起,使遊戲中的整體體驗盡可能地愉快。
為此,Photon Fusion提供了一系列的API呼叫,可以用來為尋找完美匹配的玩家創造最佳體驗。
Photon Fusion與Photon Cloud的工作是透明的,所以大部分與Photon後台服務的互動都是在內部自動完成的。
本頁介紹了Fusion Matchmaking API,該API用於創建/更新帶有自定義屬性的Game Session,玩家可以根據自己想要的遊戲體驗來過濾/加入最佳Session。
創建和加入一個遊戲會話
創建和加入Game Session是同一程序的兩個部分,規則很簡單:
1. 如果沒有指定SessionName的Session,將以該SessionName創建一個新的Session(並非在所有情況下,如下文所解釋);以及,
2. 對象加入具有該SessionName的會話。
就API而言,所有這些都是在新的Fusion Simulation啟動時自動完成的,下面列出了創建新會話時可用於訂製會話的主要參數:
C#
NetworkRunner.StartGame(new StartGameArgs {
// other args...
SessionName = [string]
SessionProperties = [Dictionary<string, int>],
CustomLobbyName = [string],
PlayerCount = [int],
DisableNATPunchthrough = [bool],
CustomSTUNServer = [string],
AuthValues = [AuthenticationValues],
DisableClientSessionCreation = [bool]
});
所有與Game Session相關的參數都是可選的,每個參數的默認值描述如下:
- SessionName:
Game Session的Name,它將識別Photon Cloud上的會話,在區域內須必須是唯一的。默認情況下,如果沒有設置Name,Fusion將生成一個隨機的GUID來識別會話,這導致了兩種可能的情況:- 當以特定的
SessionName開始時,Fusion將總是試圖以該Name創建/加入一個Session。這是關於Fusion的一個重要事實,一個Client的對象是可以創建會話的-這被稱為延遲加入伺服器,其中一個Server可以加入一個由Client事先創建的Session;以及, - 當不指定
SessionName時,只有當您在Host、Server或Shared遊戲模式下啟動時,Fusion才會創建/加入一個新的會話,如果您在Client模式下啟動,將嘗試加入一個隨機Session。
- 當以特定的
- SessionProperties:
Session的Custom Properties是包括關於您的Game Session的元數據的方式,例如遊戲模式/類型或當前加載的地圖。請記住,當創建Session時,這些屬性總是被發布到Session Lobby。這個參數的另一個用途是當一個Client加入一個隨機Session時作為匹配過濾器。作為一個建議,總是盡量保持Properties Keys字符串的長度,以減少流量。默認情況下,Session Custom Properties是空的,不包括額外的訊息。 - CustomLobbyName:
Lobby只不過是聚集類似的Game Session的一種方式,例如,它可以用來隔離不同遊戲類型的會話。這個參數用來設置自定義的Lobby Name,Session將與之關聯。默認情況下,Fusion已經根據GameMode分離了一個Session,如果Session是在Host、Server或Client遊戲模式下創建的,則在ClientServer Lobby上分離;如果是在Shared遊戲模式下創建的,則在Shared Lobby上分離。 - PlayerCount:定義可以連接到該
Session的最大客戶數量。這個參數只在創建一個新的Session時使用,默認情況下,它採用NetworkProjectConfig/Simulation上的Default Players設置的值。 - DisableNATPunchthrough:一個布林標誌,可用於禁用Photon Fusion上實施的NAT Punchthrough系統。這將強制客戶和伺服器之間的中繼連接。如果在
Game Server上設置為true,所有客戶端將通過中繼連接,如果在Client上設置,只有這個特定的對象將使用中繼連接進行連接。 - CustomSTUNServer:指定一個自定義STUN伺服器,用於解析對象的反射地址。
- AuthValues:自定義認証值,用於使用外部服務認証對象。認証是通過在
Photon Dashboard上為您的特定Application ID預先配置的服務完成的。 - DisableClientSessionCreation:一個布林標誌,用於強制執行以
Client開始的對象無法創建一個新的Session,即使指定了SessionName。默認情況下,Client能夠創建Session並等待Server加入,使用這個標誌,您可以禁用這種行為。
通過這個API,可以創建和加入一個Game Session,可以是隨機的,也可以是特定的SessionName,例如,在收到遊戲邀請時可以使用,但也可以使用自定義屬性進行Session過濾,以便只加入具有特定配置的遊戲。
這在管理會話時已經提供了很大的靈活性。
下表總結了Fusion如何處理Game Session的創建和加入,因為它取決於啟動模擬時的SessionName和GameMode。
| 有效的會話名稱 | 遊戲模式 | 行為 |
|---|---|---|
| 是 | 主機/伺服器 | 創建會話 |
| 客戶端 | 加入會話,如果找不到則失敗 | |
| 共享 | 如果沒有找到就加入會話或創建它 | |
| 否 | 主機/伺服器 | 使用隨機名稱創建會話 |
| 客戶 | 加入隨機會話,如果找不到則失敗 | |
| 共享 | 加入一個隨機會話或創建一個隨機名稱的會話(如果沒有找到) |
獲取和更新遊戲環節的訊息
Fusion提供了很多關於當前連接的Game Session的訊息,比如它的Name和Region。
這些數據可以通過SessionInfo屬性在NetworkRunner中直接使用。
下面列出了SessionInfo類型的所有可用字段:
IsValid [bool{get}]:如果SessionInfo準備好了,可以讀/寫的訊號。Name [string{get}]:SessionName。Region [string{get}]:當前連接的Region。Properties [Dictionary<string, int>{get}]:包含當前Session Custom Properties的只讀字典。為了更新這些屬性,只需使用SessionInfo.UpdateCustomProperties(Dictionary<string, int>)方法並傳遞一組新的屬性。IsVisible [bool{get,set}]:如果Session在Lobby上是Visible,則發出訊號。使一個Session不可見,只需改變這個屬性。IsOpen [bool{get,set}]:如果Session是Open的,發出訊號。為了關閉或打開一個Session,只需改變這個屬性。PlayerCount [int{get}]:Session中當前的Players數量。只有在大廳中可用。MaxPlayers [int{get}]:可以加入Session的Max Number,這個值也包括Server/Host同伴的池。僅在大廳中可用。
請記住,Session訊息,主要是Custom Properties,例如應該只用於匹配目的,決不能與遊戲客戶端同步遊戲狀態訊息。我們非常不鼓勵這種使用法。
如果您需要在整個會話中交換與遊戲有關的訊息,Fusion提供了很多選擇,比如有一個全局的NetworkObject,或者使用RPC來處理一次性數據。
NetworkRunner還提供了一些其他的Session和Photon Cloud相關的屬性,可以在遊戲中使用,例如:
NetworkRunner.IsCloudReady:如果本地對象連接到Photon Cloud並能夠創建/加入一個房間或加入一個大廳的訊號。NetworkRunner.UserId:在本地對象被認証後,持有與之相關的UserId。這個訊息來自於您的應用程式所使用的認証服務。NetworkRunner.AuthenticationValues:持有啟動Fusion時用於認証本地對象的AuthenticationValues的參考。NetworkRunner.CurrentConnectionType:描述對象當前使用的連接類型,是與遠程Server的Direct或Relayed連接。請記住,在SharedMode下,客戶端總是通過中繼連接。NetworkRunner.NATType:當啟用NAT突破系統時,Fusion將嘗試確定本地對象執行的當前網路的NAT類型,這個屬性暴露了這個訊息。NAT類型可以是:Invalid、UdpBlocked、OpenInternet、FullCone或Symmetric。NetworkRunner.IsSharedModeMasterClient:布林標誌,描述本地對象是否也是Shared Game Session的Master Client。這只在SharedMode下執行時有效,並可用於根據其他客戶端和Master Client之間的區別來確定在哪個對象中應該發生某些行動。
從大廳加入一個遊戲會話
找到正確的Game Session的另一個方法是提供一個Session列表,允許玩家選擇一個加入。
在這種情況下,加入一個Lobby是一種方法,盡管我們建議如果真的沒有必要的話,可以避免這種方法。
對於大多數遊戲類型,基於屬性過濾器加入一個Session是最好的方法,但Fusion製作Session Listing也很容易。
不使用通常的流程,而是按照上述方法啟動Fusion,Session列表遵循一個稍微不同的流程:
加入大廳:使用Fusion Runner參考,只需呼叫
NetworkRunner.JoinSessionLobby(SessionLobby, [string]),以使本地對象連接到Photon Cloud並進入一個特定的Lobby。該方法接收兩個參數。SessionLobby:可以是以下值之一。ClientServer以加入默認的ClientServer Lobby。Shared加入默認的Shared Lobby; 和,Custom,與Custom Lobby Name一起使用。
LobbyName:這應該是在創建以前的Game Session時使用的Custom Lobby Name。
獲取遊戲會話列表:當使用Fusion時,主要的API入口之一是
INetworkRunnerCallbacks,這是一個特殊的接口,Fusion用來浮現一系列不同的事件,包括來自Lobby的會話列表。OnSessionListUpdated(NetworkRunner runner, List<SessionInfo> sessionList)呼叫返回將在每次會話列表發生變化時被呼叫,無論是創建/刪除會話還是更新會話的屬性。SessionInfo與上面描述的類型相同。然後,該列表可以被顯示、過濾、排序等。加入一個會話:有了選定的要加入的
Session,可以使用通常的NetworkRunner.StartGame()來啟動Fusion,但在這種情況下,用於啟動客戶端的SessionName必須是該會話的名稱。這樣,Client將加入這個特定的Game Session。- 像往常一樣選擇正確的
GameMode,因為對象正在加入Session,它必須是GameMode.Client或GameMode.Shared模式。 SessionName字段必須設置為SessionInfo.Name,因為它是Game Session的標識符。- 所有其他的參數都是可選的,並且應該被相應地初始化,比如說
SceneObjectProvider。
- 像往常一樣選擇正確的
API使用範例
用自定義屬性開始一個新的遊戲會話
在這個例子中,Host將創建一個帶有一些自定義屬性的Game Session,所以以後Clients可以使用這些屬性過濾Session。
C#
// 一些預定義的類型被用作遊戲會話屬性的值
public enum GameType : int {
FreeForAll,
Team,
Timed
}
public enum GameMap : int {
Forest,
City,
Desert
}
// 使用定義的GameMap和GameType來啟動主機的實用方法
public async Task StartHost(NetworkRunner runner, GameMap gameMap, GameType gameType) {
var customProps = new Dictionary<string, int>();
customProps["map"] = (int)gameMap;
customProps["type"] = (int)gameType;
var result = await runner.StartGame(new StartGameArgs() {
GameMode = GameMode.Host,
SessionProperties = customProps,
});
if (result.Ok) {
// 一切順利
} else {
Debug.LogError($"Failed to Start: {result.ShutdownReason}");
}
}
範例代碼顯示了對Game Session的Custom Properties值使用Enums,但這只是為這些值添加意義的一種方法。
作為一個Host呼叫runner.StartGame(GameMode = GameMode.Host)足以啟動一個具有Random Name的新會話(因為沒有傳遞SessionName參數),通過使用SessionProperties參數,Fusion將在Session中包含這些屬性。
用過濾器加入一個隨機會話
考慮到上面的範例代碼,這裡顯示了如何啟動一個Client,它將加入任何GameMap上的任何Game Session,但有一個特定的GameMode。
啟動代碼基本相同,只是GameMode現在被設置為GameMode.Client,而customProps只包含type鍵和所需的gameType值。
C#
public async Task StartClient(NetworkRunner runner, GameType gameType) {
var customProps = new Dictionary<string, int>() {
{ "type", (int)gameType }
};
var result = await runner.StartGame(new StartGameArgs() {
GameMode = GameMode.Client,
SessionProperties = customProps,
});
if (result.Ok) {
// 一切順利
} else {
Debug.LogError($"Failed to Start: {result.ShutdownReason}");
}
}
這足以使您的客戶加入一個隨機的Session,並具有該特定的GameType。
###從大廳中加入一個會話
與其立即啟動Fusion,從Lobby中獲取Session需要另一套方法。
下面的範例代碼使Fusion Runner連接到Photon Cloud並加入預定義的ClientServer大廳。
C#
// 加入客戶端伺服器大廳的實用方法
public async Task JoinLobby(NetworkRunner runner) {
var result = await runner.JoinSessionLobby(SessionLobby.ClientServer);
if (result.Ok) {
// 一切順利
} else {
Debug.LogError($"Failed to Start: {result.ShutdownReason}");
}
}
如前所述,Client有可能以這種方式加入ClientServer、Shared或Custom大廳。
在伺服器/主機正在Custom大廳中創建一個Session的情況下,如下所示:
C#
public async Task StartHost(NetworkRunner runner) {
var result = await runner.StartGame(new StartGameArgs() {
GameMode = GameMode.Host,
CustomLobbyName = "MyCustomLobby"
});
if (result.Ok) {
// 一切順利
} else {
Debug.LogError($"Failed to Start: {result.ShutdownReason}");
}
}
在Client上加入Lobby請求需要改變為:
C#
// 加入自定義大廳的實用方法
public async Task JoinLobby(NetworkRunner runner) {
var result = await runner.JoinSessionLobby(SessionLobby.Custom, "MyCustomLobby");
if (result.Ok) {
// 一切順利
} else {
Debug.LogError($"Failed to Start: {result.ShutdownReason}");
}
}
一旦連接建立,Fusion Runner將在所有注冊的INetworkRunnerCallbacks上呼叫OnSessionListUpdated呼叫返回。
下面是一個範例代碼,顯示如何加入列表中的第一個Session。
C#
public class MyBehaviour : Fusion.Behaviour, INetworkRunnerCallbacks {
// 其他呼叫返回...
// 接收來自當前大廳的會話列表
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}");
// 這個呼叫將使Fusion作為一個客戶端加入第一個會話
runner.StartGame(new StartGameArgs() {
GameMode = GameMode.Client,
SessionName = session.Name,
SceneObjectProvider = GetSceneProvider(runner),
});
return;
}
}
}
Back to top