Matchmaking API
소개
멀티플레이어 게임을 개발할 때, 비슷한 실력, 레벨, 또는 동일한 게임 모드나 맵을 원하는 플레이어들을 쉽게 매칭 시키는 것은 중요한 요구사항입니다. 이를 통해 게임 내 경험을 최대한 즐겁게 만들 수 있습니다.
Photon Fusion은 플레이어가 최적의 매치를 찾을 수 있도록 API 호출 집합을 제공합니다.
Photon Fusion은 Photon Cloud와 투명하게 작동하며, Photon 백엔드 서비스와의 대부분의 상호작용은 내부적으로 자동 처리됩니다.
이 페이지에서는 Game Session
을 생성하고 플레이어가 원하는 게임 경험에 따라 필터링 및 참여할 수 있도록 맞춤 속성을 사용할 수 있는 Fusion Matchmaking API를 설명합니다.
용어
Matchmaking API를 올바르게 사용하려면 아래 설명된 용어를 이해하는 것이 중요합니다.
게임 세션
: 플레이어가 매치를 위해 모이는 공간입니다. 이는 Photon Cloud에 게시되며, 다른 클라이언트가 검색, 필터링 및 특정 게임에 참여할 수 있도록 제공됩니다.
세션
은 다음 속성 및 메서드를 가집니다: 이름으로 생성 및 참여 가능,사용자 지정 속성
을 가질 수 있음, 최대 플레이어 수 설정 가능, 숨김 처리(로비에서 표시되지 않음) 또는 표시 가능, 닫힘 처리(참여 불가) 또는 이전에는룸
으로 불렸던 열림 가능로비
:세션
의 가상 컨테이너 또는 "목록"입니다. 여러 로비를 사용해 다양한 게임 유형으로 세션을 분리할 수 있습니다.
클라이언트는 로비에서 다른 클라이언트와 직접적으로 통신할 수 없으며, 클라이언트는 로비,세션
, 또는 그 어떤 것도 아닌 상태 중 하나에만 있을 수 있습니다.
게임 세션 생성 및 참여
게임 세션
생성 및 참여는 동일한 절차의 두 부분이며 규칙은 간단합니다:
- 지정된
SessionName
과 동일한Session
이 없으면, 해당 이름으로 새로운세션
이 생성됩니다(아래 설명된 예외 제외). - 동일한
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
에 대한 다양한 정보를 제공합니다. 예를 들어, 세션의 Name
및 Region
등을 확인할 수 있습니다.
이 데이터는 NetworkRunner
의 SessionInfo
속성을 통해 직접 액세스할 수 있습니다.
아래는 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}]
: 현재 로비의 이름을 반환합니다.
게임 세션 브라우저
게임 세션 브라우저는 1990년대와 2000년대 초반에 인기가 있었습니다. 이러한 기본 설계는 여전히 유효하지만, 오늘날 그 주요 목적(적합한 게임을 찾아 빠르게 참여)은 매치메이킹으로 대체되었습니다.
게임 세션 브라우저를 권장하지 않는 이유는 여러 가지가 있습니다:
- 보안 문제가 존재합니다.
- 활성 게임 세션 목록을 검색하는 것은 서버에 높은 성능 부하를 발생시킵니다.
- 목록은 항상 플레이어가 현재 연결된 지역에만 특정됩니다.
- 가장 중요한 이유로, 이는 시대에 뒤떨어진 설계 방식이며 오늘날 더 나은 사용자 경험(UX) 옵션이 존재합니다.
Fusion Matchmaking API가 제공하는 최신 대안
- 빠르게 세션 채우기 또는 참여: 랜덤 오픈 룸에 참여합니다.
- 특정 유형의 매치에 참여: 게임 세션 속성을 사용하여 필터링하면서 랜덤 오픈 룸에 참여합니다.
- 특정 플레이어들과 함께 게임 참여: 초대 코드를 생성하거나 세션 이름으로 게임 세션에 참여합니다.
게임에 커뮤니티 서버가 풍부하게 존재하며, 이 서버들이 사용자 정의/모드 된 모드와 맵을 실행하는 경우가 아니라면, 전체 방 목록을 요청하여 사용자가 세션 브라우저를 통해 이를 확인하게 하는 것은 거의 필요하지 않습니다.
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.Shared
로 NetworkRunner
를 시작할 때도 동일하게 적용됩니다.
사용자 지정 속성을 사용하여 새 게임 세션 시작
이 예제에서는 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.StartGame
을 Host
로 호출(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을 시작하는 대신, 세션
목록 관리는 약간 다른 절차를 따릅니다:
- 로비 참여: Fusion Runner 참조를 사용하여
NetworkRunner.JoinSessionLobby([SessionLobby], [string])
를 호출하면 피어가 Photon Cloud에 연결되고 특정Lobby
에 참여하게 됩니다.
이 메서드는 두 개의 인수를 받습니다:SessionLobby
: 다음 값 중 하나를 선택할 수 있습니다:클라이언트서버
: 기본클라이언트 서버 로비
에 참여합니다.공유
: 기본공유 로비
에 참여합니다.사용자 지정
: 사용자 정의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}");
}
}
게임 세션 목록 가져오기: Fusion을 사용할 때 주요 API 진입점 중 하나는
INetworkRunnerCallbacks
입니다.
이는 Fusion이 다양한 이벤트를 처리하기 위해 사용하는 특별한 인터페이스로,로비
에서 세션 목록을 포함한 여러 이벤트를 제공합니다.
OnSessionListUpdated(NetworkRunner runner, List<SessionInfo> sessionList)
콜백은 세션이 생성/삭제되거나 세션 속성이 업데이트될 때마다 호출되며, 로비에서 사용 가능한 모든 세션 목록을 제공합니다.
제공된sessionList
는 앞서 설명한SessionInfo
유형으로, 이 목록을 표시하거나, 필터링하거나, 정렬하는 등 원하는 방식으로 사용할 수 있습니다.세션 참여: 선택한
세션
에 참여하기 위해, 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