이번에 새로운 멀티플레이 포트폴리오 작업 계획을 세울때 어차피 세션에 대한 개념이 들어가고 이 세션 시스템을 한번 만들어 놓으면 나중에 다른 멀티플레이 프로젝트를 할때도 많이 사용할 것 같아 이를 그냥 플러그인으로 만들어 보려고 했다.
따라서 현재 언리얼의 세션이 어떻게 돌아가는지, FAB에는 어떤 세션 플러그인들이 존재하는지, 내가 더 개선해서 플러그인으로 만들만한 부분들이 있는지 등을 먼저 조사해보았다.
IOnlineSession
은 언리얼이 세션 관련 중요 함수들을 모아놓은 인터페이스이다. 대표적으로 Session Create, Session Find, Session Join, Session Destroy, Session Start와 같은 함수들을 제공한다. 또한, 각각의 함수들이 불려졌을때 트리거되는 Delegate(i.e. FOnCreateSessionCompleteDelegate)들 또한 제공하고 있다.
보통 언리얼에서 'F-' prefix가 붙으면 구조체를 말하는데 FOnlineSessionSettings
그냥 일반 클래스여서 약간 의아했다. 이 클래스에서는 Session 관련 함수를 부를때 추가적인 설정을 더할 수 있다. 예를 들어서 IOnlineSession
에 존재하는 CreateSession은 단순하게 이렇게 생겼다.
SessionInterface->CreateSession(0, SessionName, SessionSettings);
위 코드에서 SessionSettings에 추가 정보를 입력함으로서 디테일하게 세션 관련 함수를 사용할 수 있다.
이 클래스들은 항상 중요한 친구들이지만 세션 작업시에도 이들이 어떻게 일을 하는지 잘 알고 있어야 한다. GameMode
는 Multiplay 게임에서 사용자가 들어오고 나갈때 작동하는 PostLogin, Logout 함수들이 있다. GameState
에는 Login을 완료한 연결된 플레이어의 PlayerState 배열을 들고 있어 현재 몇명이 server에 연결되어 있는지 확인할 수 있다. GameInstance
는 Singleton 객체이면서 Multiplay 게임 도중 맵이 전환될때에도 죽지 않고 살아있다(다른 객체들은 server-travel이 일어나면 없어졌다가 다시 spawn된다)
인터넷 서칭을 해보니 Joshua란 사람이 이미 FOnlineSessionSettings
의 세팅을 한꺼번에 포함해서 세션관련 함수들을 다룰 수 있는 BP 함수 라이브러리를 만들어 놓았다. 이에 관련해서 유튜브에 많은 튜토리얼 또한 존재했어서 이분이 만든 프로덕트를 먼저 사용해보기로 했다.
AdvancedSessions
와 AdvancedSteamSessions
2개 파일 이동왼쪽이 언리얼에서 기본적으로 제공하는 CreateSession BP 노드이고, 오른쪽이 AdvancedSEssionPlugin에서 만든 CreateSession BP 노드이다. FOnlineSessionSettings
에서 제공하는 추가 세팅들을 그냥 인풋핀에 박아버린 것을 확인할 수 있다.
또, 여기서 하나의 중요한 킥이 있는데 바로 Extra Settings
인풋 핀이다. 여기서는 자기가 Session을 만들때 넣고 싶은 값을 Key-Value 형태로 만들어서 보관할 수 있다. 예를 들어서 세션의 이름, 세션의 제작자, 세션을 만든 위치 등 다양한 정보를 상황에 맞게 넣을 수 있다.
여러 관련 유튜브 튜토리얼을 보면서 Pixel Helmet이라는 채널의 자료를 사용해서 플러그인 테스트를 진행해보았다.
기능은 아주 간단하다. 사용자는 세션을 만들거나 아니면 만들어진 세션 목록들을 보면서 자기가 세션에 참여할 수 있다. 세션을 만드는 창에서는 세션 이름, 최대 접속 플레이어 수, 나라, 맵 등을 설정해서 세션을 만들 수 있다.
만들어진 세션 목록들을 볼수있는 Session Browser에서는 Session을 만들때 넣었던 추가 항목들로 필터링해서 결과 값을 볼 수 있다. 여기서 디테일이 있는데, 처음 Session Browser에 진입하는 순간과 Refresh 버튼을 누르는 순간에만 생성된 모든 세션 결과를 가져와 저장한다. 이게 시간이 은근 많이 걸리는 작업임으로 필터링할때 매번 세션 결과를 가져오지 않게 했다.
첫 번째로 고전했던 부분은 Client와 Server의 초기 화면을 맞추는 것이였다. 레벨 블루프린트
에서 시네마틱 카메라와 Set View Target With Blend
를 사용해서 백그라운드에 고정된 영상을 실시간으로 틀어주는 것이 가능하다. 하지만, Server에서만 영상이 틀어지고 Client에서는 틀어지지 않는 문제가 발생했다.
이유를 찾아보니 레벨 블루프린트의 로직은 서버에서만 실행이 된다고 한다. 따라서 이를 해결하기 위해 Set View Target With Blend
관련 로직을 Client RPC로 만들어 Player Controller안에 위치 시켰다. 그리고, 레벨 블루프린트에서는 서버가 GameState에 접근해 연결된 모든 Player의 PlayerController의 Client RPC를 직접 실행시켜 주었다.(사실 여기서도 PlayerController가 로그인되기 전에 GameState를 먼저 접근해버려서 문제가 생겼는데 일단은 임시방편으로 delay를 사용해서 해결했다. PostLogin에 델리게이트를 연결하면 깔끔하게 해결할 수 있을 것 같긴 하다.)
두 번째로 고전했던 부분은 GameMode/GameModeBase와 GameState/GameStateBase의 관계였다. 둘다, ~Base가 붙지 않는 클래스가 더 확장된 기능을 제공한다. 여기서 GameMode를 상속한 클래스는 GameState를 상속한 클래스를 사용해야 되고 반대로, GameModeBase를 상속한 클래스는 GameStateBase를 상속한 클래스를 사용해야 한다. 이를 몰라서 많이 해맸다.
세 번째로 고전했던 부분은 맵이였다. 플레이어가 Session Browser에서 선택한 세션과 연관된 맵으로 client travel하려면 맵에 대한 정보를 알아야 한다. 처음에는 언리얼 맵의 타입인 UWorld의 Soft Reference를 저장하는 것이 가능해서 Reference로 직접 맵을 열려고 했으나 이름만으로 열 수 있는 방법이 있다는 것을 알았다.
이름으로 맵을 잘 열다가 프로젝트를 패키징할때 맵 관련 오류가 나서보니 게임 프로젝트에 사용되는 맵을 따로 Project Setting에 아래와 같이 등록했었어야 했다.
https://vreue4.com/advanced-sessions-plugin
https://github.com/mordentral/AdvancedSessionsPlugin
https://www.youtube.com/@PixelHelmet