This page is a work in progress and could be pending updates.

Matchmaking API

簡介

在創建多人遊戲時,一個關鍵的要求是能夠很容易地將具有類似技能、水平或想玩相同遊戲類型或地圖的玩家匹配在一起,使遊戲中的整體體驗盡可能地愉快。 為此,Photon Fusion提供了一系列的API呼叫,可以用來為尋找完美匹配的玩家創造最佳體驗。

Photon Fusion與Photon Cloud的工作是透明的,所以大部分與Photon後台服務的互動都是在內部自動完成的。 本頁介紹了Fusion Matchmaking API,該API用於創建/更新帶有自定義屬性的Game Session,玩家可以根據自己想要的遊戲體驗來過濾/加入最佳Session

Back To Top

創建和加入一個遊戲會話

創建和加入Game Session是同一程序的兩個部分,規則很簡單: 1. 如果沒有指定SessionNameSession,將以該SessionName創建一個新的Session(並非在所有情況下,如下文所解釋);以及, 2. 對象加入具有該SessionName的會話。

就API而言,所有這些都是在新的Fusion Simulation啟動時自動完成的,下面列出了創建新會話時可用於訂製會話的主要參數:

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相關的參數都是可選的,每個參數的默認值描述如下:

  • SessionNameGame SessionName,它將識別Photon Cloud上的會話,在區域內須必須是唯一的。默認情況下,如果沒有設置Name,Fusion將生成一個隨機的GUID來識別會話,這導致了兩種可能的情況:
    1. 當以特定的SessionName開始時,Fusion將總是試圖以該Name創建/加入一個Session。這是關於Fusion的一個重要事實,一個Client的對象是可以創建會話的-這被稱為延遲加入伺服器,其中一個Server可以加入一個由Client事先創建的Session;以及,
    2. 指定SessionName時,只有當您在HostServerShared遊戲模式下啟動時,Fusion才會創建/加入一個新的會話,如果您在Client模式下啟動,將嘗試加入一個隨機Session
  • SessionPropertiesSessionCustom Properties是包括關於您的Game Session的元數據的方式,例如遊戲模式/類型或當前加載的地圖。請記住,當創建Session時,這些屬性總是被發布到Session Lobby。這個參數的另一個用途是當一個Client加入一個隨機Session時作為匹配過濾器。作為一個建議,總是盡量保持Properties Keys字符串的長度,以減少流量。默認情況下,Session Custom Properties是空的,不包括額外的訊息。
  • CustomLobbyNameLobby只不過是聚集類似的Game Session的一種方式,例如,它可以用來隔離不同遊戲類型的會話。這個參數用來設置自定義的Lobby NameSession將與之關聯。默認情況下,Fusion已經根據GameMode分離了一個Session,如果Session是在HostServerClient遊戲模式下創建的,則在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的創建和加入,因為它取決於啟動模擬時的SessionNameGameMode

有效的會話名稱 遊戲模式 行為
主機/伺服器 創建會話
客戶端 加入會話,如果找不到則失敗
共享 如果沒有找到就加入會話或創建它
主機/伺服器 使用隨機名稱創建會話
客戶 加入隨機會話,如果找不到則失敗
共享 加入一個隨機會話或創建一個隨機名稱的會話(如果沒有找到)

Back To Top

獲取和更新遊戲環節的訊息

Fusion提供了很多關於當前連接的Game Session的訊息,比如它的NameRegion。 這些數據可以通過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}]:如果SessionLobby上是Visible,則發出訊號。使一個Session不可見,只需改變這個屬性。
  • IsOpen [bool{get,set}]:如果SessionOpen的,發出訊號。為了關閉或打開一個Session,只需改變這個屬性。
  • PlayerCount [int{get}]Session中當前的Players數量。只有在大廳中可用
  • MaxPlayers [int{get}]:可以加入SessionMax Number,這個值也包括Server/Host同伴的池。僅在大廳中可用

請記住,Session訊息,主要是Custom Properties,例如應該只用於匹配目的決不能與遊戲客戶端同步遊戲狀態訊息。我們非常不鼓勵這種使用法。 如果您需要在整個會話中交換與遊戲有關的訊息,Fusion提供了很多選擇,比如有一個全局的NetworkObject,或者使用RPC來處理一次性數據。

NetworkRunner還提供了一些其他的SessionPhoton Cloud相關的屬性,可以在遊戲中使用,例如:

  • NetworkRunner.IsCloudReady:如果本地對象連接到Photon Cloud並能夠創建/加入一個房間或加入一個大廳的訊號。
  • NetworkRunner.UserId:在本地對象被認証後,持有與之相關的UserId。這個訊息來自於您的應用程式所使用的認証服務。
  • NetworkRunner.AuthenticationValues:持有啟動Fusion時用於認証本地對象的AuthenticationValues的參考。
  • NetworkRunner.CurrentConnectionType:描述對象當前使用的連接類型,是與遠程ServerDirectRelayed連接。請記住,在SharedMode下,客戶端總是通過中繼連接。
  • NetworkRunner.NATType:當啟用NAT突破系統時,Fusion將嘗試確定本地對象執行的當前網路的NAT類型,這個屬性暴露了這個訊息。NAT類型可以是:InvalidUdpBlockedOpenInternetFullConeSymmetric
  • NetworkRunner.IsSharedModeMasterClient:布林標誌,描述本地對象是否也是Shared Game SessionMaster Client。這只在SharedMode下執行時有效,並可用於根據其他客戶端和Master Client之間的區別來確定在哪個對象中應該發生某些行動。

Back To Top

從大廳加入一個遊戲會話

找到正確的Game Session的另一個方法是提供一個Session列表,允許玩家選擇一個加入。 在這種情況下,加入一個Lobby是一種方法,盡管我們建議如果真的沒有必要的話,可以避免這種方法。 對於大多數遊戲類型,基於屬性過濾器加入一個Session是最好的方法,但Fusion製作Session Listing也很容易。

不使用通常的流程,而是按照上述方法啟動Fusion,Session列表遵循一個稍微不同的流程:

  1. 加入大廳:使用Fusion Runner參考,只需呼叫NetworkRunner.JoinSessionLobby(SessionLobby, [string]),以使本地對象連接到Photon Cloud並進入一個特定的Lobby。該方法接收兩個參數。

    • SessionLobby:可以是以下值之一。
      1. ClientServer以加入默認的ClientServer Lobby
      2. Shared加入默認的Shared Lobby; 和,
      3. Custom,與Custom Lobby Name一起使用。
    • LobbyName:這應該是在創建以前的Game Session時使用的Custom Lobby Name
  2. 獲取遊戲會話列表:當使用Fusion時,主要的API入口之一是INetworkRunnerCallbacks,這是一個特殊的接口,Fusion用來浮現一系列不同的事件,包括來自Lobby的會話列表。OnSessionListUpdated(NetworkRunner runner, List<SessionInfo> sessionList)呼叫返回將在每次會話列表發生變化時被呼叫,無論是創建/刪除會話還是更新會話的屬性。SessionInfo與上面描述的類型相同。然後,該列表可以被顯示、過濾、排序等。

  3. 加入一個會話:有了選定的要加入的Session,可以使用通常的NetworkRunner.StartGame()來啟動Fusion,但在這種情況下,用於啟動客戶端的SessionName必須是該會話的名稱。這樣,Client將加入這個特定的Game Session
    • 像往常一樣選擇正確的GameMode,因為對象正在加入Session,它必須是GameMode.ClientGameMode.Shared模式。
    • SessionName字段必須設置為SessionInfo.Name,因為它是Game Session的標識符。
    • 所有其他的參數都是可選的,並且應該被相應地初始化,比如說SceneObjectProvider

Back To Top

API使用範例

用自定義屬性開始一個新的遊戲會話

在這個例子中,Host將創建一個帶有一些自定義屬性的Game Session,所以以後Clients可以使用這些屬性過濾Session

// 一些預定義的類型被用作遊戲會話屬性的值
  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 SessionCustom Properties值使用Enums,但這只是為這些值添加意義的一種方法。 作為一個Host呼叫runner.StartGameGameMode = GameMode.Host)足以啟動一個具有Random Name的新會話(因為沒有傳遞SessionName參數),通過使用SessionProperties參數,Fusion將在Session中包含這些屬性。

Back To Top

用過濾器加入一個隨機會話

考慮到上面的範例代碼,這裡顯示了如何啟動一個Client,它將加入任何GameMap上的任何Game Session,但有一個特定的GameMode。 啟動代碼基本相同,只是GameMode現在被設置為GameMode.Client,而customProps只包含type鍵和所需的gameType值。

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

Back To Top

從大廳中加入一個會話

與其立即啟動Fusion,從Lobby中獲取Session需要另一套方法。 下面的範例代碼使Fusion Runner連接到Photon Cloud並加入預定義的ClientServer大廳。

// 加入客戶端伺服器大廳的實用方法
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有可能以這種方式加入ClientServerSharedCustom大廳。 在伺服器/主機正在Custom大廳中創建一個Session的情況下,如下所示:

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請求需要改變為:

// 加入自定義大廳的實用方法
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

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;
    }
  }
}


To Document Top