Deeper Learning

[UE5] Online Subsystem (Session Interface를 사용한 Steam 멀티플레이 구현) 본문

Game Development/Unreal Engine

[UE5] Online Subsystem (Session Interface를 사용한 Steam 멀티플레이 구현)

Dlaiml 2024. 6. 26. 19:48

 

 

[UE5] 클라이언트-서버 모델

언리얼 엔진에서 싱글 플레이 게임에 대해서만 학습하다 보니 멀티플레이 게임, 네트워크에 대해 알고 싶어져 멀티플레이에 대해 학습을 시작하였다. 이번 포스팅에서는 언리얼 엔진의 멀티플

dlaiml.tistory.com

 

지난 포스팅에서 LAN으로 간단하게 멀티 플레이 환경을 구현해 보았다. 하지만 실제로 다른 지역의 사람들과 게임을 같이하려면 신경 쓸 것들이 매우 많아진다. (세션관리, 매치메이킹, 보안, 여러 플랫폼 지원 등) 

 

Steam, XBox Live, Epic Online Services, PlayStation Network 등 온라인 서비스를 지원하는 다양한 플랫폼이 있는데, 각자 지원하는 기능이 다르고 명칭도 다르다. 온라인 서브시스템은 기능들에 공통적으로 접근할 수 있도록 해준다.

 

언리얼 엔진 개발자가 여러 플랫폼에 게임을 출시할 때 각 플랫폼에 맞는 온라인 지원 코드를 모두 따로 작성하지 않고 통합된 코드로 이를 해결하도록 하려면 저번 포스팅의 주제였던 Interface가 필요하다

 

 

[UE5] Interface를 활용한 의존성 관리 (Dependency Inversion)

객체지향에서 종속성 관리는 매우 중요하다. 다른 클래스의 method를 사용하기 위해 여러 종속성을 주입하다 보면 결국 클래스의 본 목적과 달리 다른 클래스가 없이는 사용할 수 없는 클래스가

dlaiml.tistory.com

 

친구기능을 담당하는 인터페이스가 있다면 친구추가, 친구삭제와 같은 기능이 각 플랫폼의 온라인 지원 모듈에 자세한 로직이 작성되어 있을 것이다.

언리얼 엔진 개발자는 친구 인터페이스의 함수 호출만 하면 쉽게 여러 플랫폼에서 친구추가, 친구삭제 기능을 게임에 넣을 수 있게 된다.

 

온라인 환경에서 필요한 인터페이스들을 모아 구성한 시스템이 바로 온라인 서브시스템이다.

Achievements 게임 업적 인터페이스
External UI 특정 하드웨어, 온라인 서비스에 내장된 외부 UI 인터페이스
Friends 친구 인터페이스
Leaderboard 점수를 확인할 수 있는 온라인 리더보드 인터페이스
Online User 사용자 메타데이터 수집 인터페이스
Presence 온라인 상태(자리비움, 플레이 중, 온라인) 인터페이스
Purchase 게임 내 구매 인터페이스
Session 온라인 게임 세션 인터페이스 (세션 생성, 소멸, 검색, 매치메이킹)
Store 스토어 인터페이스 (게임 내 구매, 카테고리 추천)
User Cloud 유저 클라우드 파일 저장 인터페이스

 

네트워크 환경에 영향을 받는 온라인 서비스의 특성상 온라인 서브시스템은 모든 원격 작업에 Delegate를 사용하는 것을 원칙으로 설계되었다.

 

Online Subsystem - Session

Session 인터페이스를 예시로 사용법을 간단하게 알아보자.

 

우선 빌드파일의 모듈이름에 OnlineSubsystem을 추가한다.  

 

Config의 DefaultEngine 설정 파일에 기본으로 설정 플랫폼 서비스를 지정한다.

개발 단계에서는 NULL로 설정하여 에디터에서도 쉽게 디버깅할 수 있도록 하자.

 

# {ProjectName}.Build.cs
PublicDependencyModuleNames.AddRange(new string[] { 
"Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput", "UMG", "OnlineSubsystem"
});



#{ProjectRoot}/Config/DefaultEngine.ini

[OnlineSubsystem]
DefaultPlatformService=NULL

 

 

 

게임 전반과 관련된 멀티플레이 로직이 포함되므로 GameInstance 클래스에 코드를 작성하려 한다. 

#include "OnlineSubsystem.h"
#include "Interfaces/OnlineSessionInterface.h"
#include "OnlineSessionSettings.h"


IOnlineSessionPtr SessionInterface;
TSharedPtr<class FOnlineSessionSearch> SessionSearch;

 

사용할 온라인 서브시스템, 온라인 세션 인터페이스의 헤더를 GameInstance에 include 하였고

SessionInterface, SessionSearch를 멤버로 선언하였다.

 

 

// UCustomGameInstance.cpp

void UCustomGameInstance::CreateSession()
{
	if (SessionInterface.IsValid())
	{
		FOnlineSessionSettings SessionSettings;
		SessionSettings.NumPublicConnections = 2;
		SessionSettings.bShouldAdvertise = true;
		SessionSettings.bUsesPresence = true;
		SessionInterface->CreateSession(0, TEXT("SESSION NAME!"), SessionSettings);
	}
}


void UCustomGameInstance::Init()
{
    IOnlineSubsystem* OnlineSubsystemInterface = IOnlineSubsystem::Get();
	if (nullptr != OnlineSubsystemInterface)
	{
		SessionInterface = OnlineSubsystemInterface->GetSessionInterface();
		if (SessionInterface.IsValid())
		{
			SessionInterface->OnCreateSessionCompleteDelegates.AddUObject(this, &UCustomGameInstance::OnCreateSessionComplete);
			SessionInterface->OnDestroySessionCompleteDelegates.AddUObject(this, &UCustomGameInstance::OnDestroySessionComplete);
			SessionInterface->OnFindSessionsCompleteDelegates.AddUObject(this, &UCustomGameInstance::OnFindSessionsComplete);
			SessionInterface->OnJoinSessionCompleteDelegates.AddUObject(this, &UCustomGameInstance::OnJoinSessionComplete);

		}
	}
}

 

Init 함수를 먼저 살펴보자

 

 

온라인 서브시스템은 IOnlineSubsyetem::Get()을 통해 쉽게 가져올 수 있다. (내부 코드를 보면 Singleton으로 구현되어 있다) 

불러온 온라인 서브시스템 인터페이스에서 GetSessionInterface로 SessionInterface를 불러온다.

이후 앞서 말한 것처럼 Delegate를 사용하기 때문에 세션 생성, 세션 소멸, 세션 탐색, 세션 참가 완료 시점에 호출되는 Delegate에 직접 작성한 함수들을 바인딩하였다.

 

CreateSession 함수는 세션 생성을 담당하는 함수이다.

SessionInteraface가 유효한지 확인하고 세션 생성 구조체에서 세션과 관련 설정을 입력 후 SessionInterface의 CreateSession을 호출한다.

 

SessionInterface의 CreateSession 호출 후 아래 순서대로 호출이 진행된다.

SessionInterface CreateSession ->
Platform service(NULL)의 CreateSession ->
Session Interface의 Delegate OnCreateSessionCompleteDelegates ->
직접 바인딩한 함수인 UCustomGameInstance::OnCreateSessionComplete 

 

 

세션을 찾고 찾은 세션 중 선택한 세션에 참가하는 코드 부분도 아래와 같이 SessionInterface를 사용한다.

// Find Session 
SessionInterface->FindSessions(0, SessionSearch.ToSharedRef());

// Join Session
SessionInterface->JoinSession(0, SESSION_NAME, SessionSearch->SearchResults[Index]);

 

직접 Local IP 주소를 입력해서 접속하던 방식에서 OSS(Online Subsystem)을 사용하는 방식으로 멀티플레이 환경을 변경하였다.

 

여기에서 이제 온라인 서브시스템의 가장 큰 장점이 드러나는데 UE 에디터에서 Steam 플러그인을 설치하고 아래 코드블럭처럼 모듈에 OnlineSubsystemSteam, 설정파일의 NULL을 Steam으로 바꾸면 같은 코드로 Steam의 온라인 서비스를 이용할 수 있게 된다.

 

 

Steam 온라인 서브시스템을 위한 설정 파일의 수정은 공식 문서를 참고하였다.

https://dev.epicgames.com/documentation/en-us/unreal-engine/online-subsystem-steam-interface-in-unreal-engine?application_version=5.3

 

//{ProjectName}.Build.cs
PublicDependencyModuleNames.AddRange(new string[] { 
"Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput", "UMG", "OnlineSubsystem", "OnlineSubsystemSteam"
});


// https://dev.epicgames.com/documentation/en-us/unreal-engine/online-subsystem-steam-interface-in-unreal-engine?application_version=5.3
// DefaultEngine.ini
[/Script/Engine.GameEngine]
+NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="OnlineSubsystemSteam.SteamNetDriver",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver")
	 	
[OnlineSubsystem]
DefaultPlatformService=Steam
	 
[OnlineSubsystemSteam]
bEnabled=true
SteamDevAppId=480

; If using Sessions
; bInitServerOnClient=true

[/Script/OnlineSubsystemSteam.SteamNetDriver]
NetConnectionClassName="OnlineSubsystemSteam.SteamNetConnection"

 

 

Reference

[0] https://docs.unrealengine.com/4.27/en-US/ProgrammingAndScripting/Online/

 

Online Subsystem

Overview of the various systems related to the online platform.

docs.unrealengine.com

 

[1] https://dev.epicgames.com/documentation/en-us/unreal-engine/online-subsystem-steam-interface-in-unreal-engine?application_version=5.3

 

Comments