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