This document is about: FUSION 2
SWITCH TO

Matchmaking API

소개

멀티플레이어 게임을 개발할 때, 비슷한 실력, 레벨, 또는 동일한 게임 모드나 맵을 원하는 플레이어들을 쉽게 매칭 시키는 것은 중요한 요구사항입니다. 이를 통해 게임 내 경험을 최대한 즐겁게 만들 수 있습니다.
Photon Fusion은 플레이어가 최적의 매치를 찾을 수 있도록 API 호출 집합을 제공합니다.

Photon Fusion은 Photon Cloud와 투명하게 작동하며, Photon 백엔드 서비스와의 대부분의 상호작용은 내부적으로 자동 처리됩니다.
이 페이지에서는 Game Session을 생성하고 플레이어가 원하는 게임 경험에 따라 필터링 및 참여할 수 있도록 맞춤 속성을 사용할 수 있는 Fusion Matchmaking API를 설명합니다.

용어

Matchmaking API를 올바르게 사용하려면 아래 설명된 용어를 이해하는 것이 중요합니다.

  • 게임 세션: 플레이어가 매치를 위해 모이는 공간입니다. 이는 Photon Cloud에 게시되며, 다른 클라이언트가 검색, 필터링 및 특정 게임에 참여할 수 있도록 제공됩니다.
    세션은 다음 속성 및 메서드를 가집니다: 이름으로 생성 및 참여 가능, 사용자 지정 속성을 가질 수 있음, 최대 플레이어 수 설정 가능, 숨김 처리(로비에서 표시되지 않음) 또는 표시 가능, 닫힘 처리(참여 불가) 또는 이전에는 으로 불렸던 열림 가능

  • 로비: 세션의 가상 컨테이너 또는 "목록"입니다. 여러 로비를 사용해 다양한 게임 유형으로 세션을 분리할 수 있습니다.
    클라이언트는 로비에서 다른 클라이언트와 직접적으로 통신할 수 없으며, 클라이언트는 로비, 세션, 또는 그 어떤 것도 아닌 상태 중 하나에만 있을 수 있습니다.

게임 세션 생성 및 참여

게임 세션 생성 및 참여는 동일한 절차의 두 부분이며 규칙은 간단합니다:

  1. 지정된 SessionName과 동일한 Session이 없으면, 해당 이름으로 새로운 세션이 생성됩니다(아래 설명된 예외 제외).
  2. 동일한 SessionName이 있는 경우, 클라이언트는 해당 세션에 참여합니다.

API 수준에서는 새로운 Fusion 시뮬레이션을 시작할 때 자동으로 처리됩니다. 아래는 새 Session을 생성하거나 특정 Session에 참여할 때 사용할 수 있는 주요 매개변수 목록입니다:

C#

NetworkRunner.StartGame(new StartGameArgs {
  // ...
  // Arguments related to the Matchmaking between peers
  SessionName = [string],
  SessionProperties = [Dictionary<string, SessionProperty>],
  CustomLobbyName = [string],
  EnableClientSessionCreation = [bool],
  PlayerCount = [int],
  IsOpen = [bool],
  IsVisible = [bool],
  MatchmakingMode = [MatchmakingMode],
});

모든 매치메이킹 관련 매개변수는 선택 사항이며, 각 매개변수의 기본값은 아래에 설명되어 있습니다:

  • SessionName: 게임 세션Name 또는 ID로, Photon Cloud에서 세션을 식별하는 데 사용됩니다. 동일 지역 내에서 고유해야 합니다. 이름이 설정되지 않은 경우, Fusion이 임의의 GUID를 생성하여 세션을 식별합니다.
  • SessionProperties: 세션사용자 지정 속성으로, 게임 모드/유형, 현재 맵과 같은 메타데이터를 게임 세션에 포함시키는 방법입니다. 모든 속성은 세션 생성 시 항상 로비에 게시되며, 클라이언트가 랜덤 세션에 참여할 때 필터로 사용할 수 있습니다.
    팁: 트래픽을 최소화하기 위해 속성 키를 가능한 짧게 유지하세요. 기본적으로 세션 사용자 지정 속성은 비어 있으며 추가 정보가 포함되지 않습니다.
  • CustomLobbyName: 세션이 연결될 맞춤 로비 이름을 설정하는 데 사용됩니다. 기본적으로 Fusion은 GameMode에 따라 세션을 나눕니다. (호스트, 서버, 또는 클라이언트 모드 시작 시 클라이언트 서버 로비, 공유 모드 시작 시 공유 로비)
  • EnableClientSessionCreation: 어떤 클라이언트 유형이 새 게임 세션을 생성하거나 단순히 참여할 수 있는지를 결정하는 플래그입니다. 자세한 내용은 아래를 참조하세요.
  • PlayerCount: 세션에 참여할 수 있는 최대 클라이언트 수를 정의합니다. 이 매개변수는 새 세션을 생성할 때만 사용되며, 기본적으로 NetworkProjectConfig/Simulation디폴트 플레이어 필드 값을 사용합니다.
  • IsOpen: 생성 중인(해당하는 경우) 게임 세션이 다른 플레이어에게 열려 있는지 여부를 정의합니다. 자세한 내용은 세션 정보 가져오기 및 업데이트를 참조하세요.
  • IsVisible: 생성 중인(해당하는 경우) Game Session이 다른 플레이어에게 표시되는지 여부를 정의합니다. 자세한 내용은 세션 정보 가져오기 및 업데이트를 참조하세요.
  • MatchmakingMode: 랜덤 Game Session에 참여할 때 사용하는 매칭 모드를 정의합니다.
    • FillRoom: 가능한 한 빨리 플레이어를 모으기 위해 방을 (오래된 순서로) 채웁니다. 기본값.
    • SerialMatching: 필터를 고려하여 사용 가능한 방에 순차적으로 플레이어를 분배합니다. 필터가 없을 경우, 방에 플레이어가 고르게 분포됩니다.
    • RandomMatching: 완전히 랜덤한 방에 참여합니다. 예상 속성이 일치해야 하지만 이 외에는 모든 사용 가능한 방이 선택될 수 있습니다.

게임 세션을 랜덤으로 생성하거나 특정 SessionName으로 생성 및 참여할 수 있습니다(예: 게임 초대 시 유용). 또한, 맞춤 속성을 사용한 Session 필터링을 통해 특정 설정이 있는 게임에만 참여할 수 있습니다.
이 기능은 세션 관리에 많은 유연성을 제공합니다.

아래 표는 Fusion이 SessionName, GameMode, 그리고 시뮬레이션 시작 시 EnableClientSessionCreation의 활성화 여부에 따라 게임 세션 생성 및 참여를 처리하는 방식을 요약한 것입니다.

게임 모드 세션 이름
유효 비어있음/Null
서버/호스트 지정한 세션 생성 또는 참여 무작위 ID로 생성 또는 세션 참여
EnableClientSessionCreation
Null (기본) True False Null (기본) True False
클라이언트 세션 참여 세션 생성 또는 참여 세션 참여 무작위 세션 참여 무작위 참여 또는 생성 무작위 세션 참여
공유 생성 또는 세션 참여 생성 또는 세션 참여 세션 참여 무작위 참여 또는 생성 무작위 참여 또는 생성 무작위 참여
AutoHostOrClient 생성 또는 세션 참여 생성 또는 세션 참여 세션 참여 무작위 참여 또는 생성 무작위 참여 또는 생성 무작위 세션 참여

게임 세션 정보 가져오기 및 업데이트

Fusion은 현재 연결된 Game Session에 대한 다양한 정보를 제공합니다. 예를 들어, 세션의 NameRegion 등을 확인할 수 있습니다.
이 데이터는 NetworkRunnerSessionInfo 속성을 통해 직접 액세스할 수 있습니다.
아래는 SessionInfo 타입의 모든 사용 가능한 필드를 나열한 것입니다:

  • IsValid [bool{get}]: SessionInfo가 읽기/쓰기 가능한 상태인지 나타냅니다.
  • Name [string{get}]: 세션 이름을 반환합니다.
  • Region [string{get}]: 현재 연결된 지역을 반환합니다.
  • Properties [Dictionary<string, SessionProperty>{get}]: 현재 Session Custom Properties를 포함하는 읽기 전용 사전.
    속성을 업데이트하려면 SessionInfo.UpdateCustomProperties(Dictionary<string, SessionProperty>) 메서드를 사용하여 새로운 속성 세트를 전달하십시오.
  • IsVisible [bool{get,set}]: 세션이 Lobby에서 Visible 상태인지 나타냅니다. 세션을 숨기려면 이 속성을 변경하면 됩니다.
  • IsOpen [bool{get,set}]: 세션이 다른 플레이어에게 열려 있는지 여부를 나타냅니다. 세션을 열거나 닫으려면 이 속성을 변경하면 됩니다.
  • PlayerCount [int{get}]: 세션에 있는 현재 플레이어 수를 반환합니다. (로비에서만 사용 가능)
  • MaxPlayers [int{get}]: 세션에 참여할 수 있는 최대 피어 수를 반환합니다. 이 값은 Server/Host 피어의 슬롯도 포함합니다. (로비에서만 사용 가능)

Session 정보, 특히 Custom Properties매치메이킹 목적으로만 사용해야 하며, 게임 클라이언트 간의 게임 상태 정보를 동기화하는 데 사용해서는 안 됩니다.
이러한 사용은 강력히 권장하지 않습니다.
세션 관련 정보를 공유하거나 게임 플레이와 관련된 데이터를 교환해야 하는 경우, Fusion은 글로벌 NetworkObject를 사용하거나 RPC를 통해 단발성 데이터를 전달하는 등 다양한 옵션을 제공합니다.

NetworkRunner는 게임 내에서 사용할 수 있는 추가 속성을 제공합니다:

  • NetworkRunner.IsCloudReady: 로컬 피어가 Photon Cloud에 연결되어 생성/참여 또는 로비 참여가 가능한지 여부를 나타냅니다.

  • NetworkRunner.UserId: 인증 후 로컬 피어와 연결된 UserId를 포함합니다. 이 정보는 애플리케이션에서 사용하는 인증 서비스에서 가져옵니다.

  • NetworkRunner.AuthenticationValues: Fusion 시작 시 로컬 피어를 인증하는 데 사용된 AuthenticationValues의 참조를 포함합니다.

  • NetworkRunner.CurrentConnectionType: 피어가 원격 서버와의 Direct 또는 Relayed 연결을 사용 중인지 설명합니다.
    참고: SharedMode에서는 클라이언트가 항상 릴레이를 통해 연결됩니다.
    이는 SharedMode에서만 유효하며, 특정 작업이 다른 클라이언트와 Master Client 중 어느 피어에서 실행되어야 하는지를 결정하는 데 사용됩니다.

  • NetworkRunner.NATType: NAT 펀치스루 시스템이 활성화된 경우, Fusion은 로컬 피어가 실행 중인 현재 네트워크의 NAT 유형을 확인하려고 시도하며, 이 속성은 해당 정보를 노출합니다.
    NAT 유형은 다음과 같습니다: Invalid, UdpBlocked, OpenInternet,FullCone,Symmetric

  • NetworkRunner.IsSharedModeMasterClient: 로컬 피어가 공유 게임 세션마스터 클라이언트인지 여부를 나타내는 불리언 플래그입니다.
    이는 SharedMode에서만 유효하며, 다른 클라이언트와 마스터 클라이언트를 구분하여 특정 작업이 실행될 피어를 결정하는 데 사용할 수 있습니다.

또 다른 관련 필드는 NetworkRunner.LobbyInfo로, 로컬 피어가 연결된 현재 로비에 대한 정보를 제공합니다.
(참고: 피어는 로비, 게임 세션, 또는 연결 해제된 상태 중 하나에만 있을 수 있습니다.)
아래는 LobbyInfo의 주요 속성을 나열한 것입니다:

  • IsValid [bool{get}]: LobbyInfo가 읽기/쓰기 가능한 상태인지 나타냅니다.
  • Name [string{get}]: 현재 로비의 이름을 반환합니다.

게임 세션 브라우저

Fusion에서 게임 세션 브라우저를 생성하는 것은 가능하지만, 강력히 권장되지 않습니다.

게임 세션 브라우저는 1990년대와 2000년대 초반에 인기가 있었습니다. 이러한 기본 설계는 여전히 유효하지만, 오늘날 그 주요 목적(적합한 게임을 찾아 빠르게 참여)은 매치메이킹으로 대체되었습니다.

게임 세션 브라우저를 권장하지 않는 이유는 여러 가지가 있습니다:

  • 보안 문제가 존재합니다.
  • 활성 게임 세션 목록을 검색하는 것은 서버에 높은 성능 부하를 발생시킵니다.
  • 목록은 항상 플레이어가 현재 연결된 지역에만 특정됩니다.
  • 가장 중요한 이유로, 이는 시대에 뒤떨어진 설계 방식이며 오늘날 더 나은 사용자 경험(UX) 옵션이 존재합니다.

Fusion Matchmaking API가 제공하는 최신 대안

  1. 빠르게 세션 채우기 또는 참여: 랜덤 오픈 룸에 참여합니다.
  2. 특정 유형의 매치에 참여: 게임 세션 속성을 사용하여 필터링하면서 랜덤 오픈 룸에 참여합니다.
  3. 특정 플레이어들과 함께 게임 참여: 초대 코드를 생성하거나 세션 이름으로 게임 세션에 참여합니다.

게임에 커뮤니티 서버가 풍부하게 존재하며, 이 서버들이 사용자 정의/모드 된 모드와 맵을 실행하는 경우가 아니라면, 전체 방 목록을 요청하여 사용자가 세션 브라우저를 통해 이를 확인하게 하는 것은 거의 필요하지 않습니다.

API 사용 예제

무작위 세션 참여

플레이어가 특정 설정 없이 빠르게 게임에 참여하고자 할 때, NetworkRunner를 시작하여 추가 매개변수 없이 사용 가능한 게임 세션을 찾도록 설정하면 됩니다:

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

이 방법을 사용하면 로컬 피어는 랜덤 게임 세션을 시작하고 연결됩니다. 만약 사용 가능한 세션이 없다면, 랜덤 세션 이름을 사용하여 새 세션을 생성합니다(이는 GameMode.AutoHostOrClient를 사용하기 때문입니다).
이 방식은 Shared Mode 세션에서도 유효하며, GameMode.SharedNetworkRunner를 시작할 때도 동일하게 적용됩니다.

사용자 지정 속성을 사용하여 새 게임 세션 시작

이 예제에서는 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사용자 지정 속성 값으로 Enums를 사용하는 예를 보여줍니다. 그러나 이는 값에 의미를 부여하는 여러 방법 중 하나일 뿐입니다.

runner.StartGameHost로 호출(GameMode = GameMode.Host)하면, SessionName 매개변수를 전달하지 않아도 Random Name을 사용하여 새 세션을 시작할 수 있습니다. 또한 SessionProperties 매개변수를 사용하면 Fusion이 해당 속성을 세션에 포함시킵니다.

필터로 무작위 세션에 참여

위의 예제 코드를 참고하여, 여기에서는 특정 GameType을 가진 GameMap의 아무 게임 세션에나 참여할 수 있는 클라이언트를 시작하는 방법을 보여줍니다.

시작 코드의 기본 구조는 동일하지만, GameMode가 이제 GameMode.Client로 설정되고, customProps에는 원하는 gameType 값을 포함하는 type 키만 추가됩니다.

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을 가진 랜덤 세션에 참여할 수 있습니다.

로비에서 세션 참여

적절한 게임 세션을 찾는 또 다른 방법은 세션 목록을 제공하여 플레이어가 참여할 세션을 선택할 수 있게 하는 것입니다.
이 경우 로비에 참여하는 것이 올바른 접근 방법이지만, 정말 필요하지 않은 경우 이 방법은 사용하지 않는 것을 강력히 권장합니다.
대부분의 게임 유형에서는 속성 필터를 기반으로 세션에 참여하는 것이 최선의 방법이지만, Fusion은 세션 목록을 관리하는 기능도 제공합니다.

일반적인 흐름으로 Fusion을 시작하는 대신, 세션 목록 관리는 약간 다른 절차를 따릅니다:

  1. 로비 참여: Fusion Runner 참조를 사용하여 NetworkRunner.JoinSessionLobby([SessionLobby], [string])를 호출하면 피어가 Photon Cloud에 연결되고 특정 Lobby에 참여하게 됩니다.
    이 메서드는 두 개의 인수를 받습니다:
    • SessionLobby: 다음 값 중 하나를 선택할 수 있습니다:
      1. 클라이언트서버: 기본 클라이언트 서버 로비에 참여합니다.
      2. 공유: 기본 공유 로비에 참여합니다.
      3. 사용자 지정: 사용자 정의 LobbyName과 함께 사용됩니다.
    • LobbyName: 이전에 게임 세션을 생성할 때 사용된 사용자 정의 로비 이름이어야 합니다.

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

앞서 설명한 것처럼, 클라이언트는 이 방법을 통해 클라이언트 서버, 공유 또는 사용자 지정 로비에 참여할 수 있습니다.
다음은 예로, 서버/호스트가 사용자 정의 로비에서 세션을 생성하는 방법을 보여줍니다:

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가 해당 사용자 정의 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}");
  }
}
  1. 게임 세션 목록 가져오기: Fusion을 사용할 때 주요 API 진입점 중 하나는 INetworkRunnerCallbacks입니다.
    이는 Fusion이 다양한 이벤트를 처리하기 위해 사용하는 특별한 인터페이스로, 로비에서 세션 목록을 포함한 여러 이벤트를 제공합니다.
    OnSessionListUpdated(NetworkRunner runner, List<SessionInfo> sessionList) 콜백은 세션이 생성/삭제되거나 세션 속성이 업데이트될 때마다 호출되며, 로비에서 사용 가능한 모든 세션 목록을 제공합니다.
    제공된 sessionList는 앞서 설명한 SessionInfo 유형으로, 이 목록을 표시하거나, 필터링하거나, 정렬하는 등 원하는 방식으로 사용할 수 있습니다.

  2. 세션 참여: 선택한 세션에 참여하기 위해, Fusion은 일반적인 NetworkRunner.StartGame()을 사용하여 시작할 수 있습니다.
    이 경우, 클라이언트를 시작할 때 SessionName은 반드시 참여하려는 세션의 이름으로 설정되어야 합니다. 이렇게 하면 클라이언트가 해당 게임 세션에 참여할 수 있습니다.

    • 적절한 게임 모드를 선택해야 합니다. 피어가 세션에 참여하므로, GameMode.Client 또는 GameMode.Shared 중 하나여야 합니다.
    • SessionName 필드는 SessionInfo.Name으로 설정해야 하며, 이는 해당 게임 세션의 식별자입니다.
    • 나머지 매개변수는 선택 사항이며 필요에 따라 초기화할 수 있습니다.

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)");

    // Example
    // Join first session from the list

    // Check if there are any Sessions to join
    if (sessionList.Count > 0) {

      // Get first Session from the list
      var session = sessionList[0];

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

      // Join
      runner.StartGame(new StartGameArgs() {
        GameMode = GameMode.Client, // Client GameMode, could be Shared as well
        SessionName = session.Name, // Session to Join
        // ...
      });
    }

    // OR

    // Example
    // Search the list for a Session with a specific Property

    // Store the target session
    SessionInfo session = null;

    foreach (var sessionItem in sessionList) {

      // Check for a specific Custom Property
      if (sessionItem.Properties.TryGetValue("type", out var propertyType) && propertyType.IsInt) {

        var gameType = (int)propertyType.PropertyValue;

        // Check for the desired Game Type
        if (gameType == 1) {

          // Store the session info
          session = sessionItem;
          break;
        }
      }
    }

    // Check if there is any valid session
    if (session != null) {
      Debug.Log($"Joining {session.Name}");

      // Join
      runner.StartGame(new StartGameArgs() {
        GameMode = GameMode.Client, // Client GameMode, could be Shared as well
        SessionName = session.Name, // Session to Join
        // ...
      });
    }
  }
}
Back to top