[Unreal Engine] Split Screen

Imeamangryang·2025년 6월 30일

Unreal UMG & Slate UI

목록 보기
12/13
post-thumbnail

Split Screen

Split Screen은 Game Layer Manager가 뷰포트의 Slate 위젯, 해당 Slate 위젯을 사용하는 플레이어, 그리고 Slate 위젯의 형태(분할 레이아웃 등)를 관리하는 구조로 동작합니다.
Game Layer Manager는 엔진 초기화 시 GameEngine.cpp(UGameEngine)와 PlayLevel.cpp(UEditorEngine)에서 하드코딩된 클래스로 인스턴스화됩니다.
따라서 커스텀 Game Layer Manager를 만들려면 엔진 소스의 UGameEngine::CreateGameViewportWidgetUEditorEngine::GeneratePIEViewportWindow를 수정해야 합니다.
(Epic에서 이 부분을 프로젝트 세팅에서 지정 가능하도록 개선해주길 바란다는 의견도 있습니다. Game Viewport Client는 이미 설정 가능하니 절반은 구현된 셈입니다.)

참고: 상황에 따라 UGameViewportClient::SetGameLayerManager를 호출해 Game Layer Manager를 교체할 수도 있지만, 엔진의 비가상(오버라이드 불가) 코드와의 호환성 문제 등 추가적인 워크어라운드가 필요할 수 있습니다.

위젯을 화면에 표시할 때는 두 가지 방법이 있습니다:

  • Add to Viewport: 전체 게임 뷰포트에 위젯을 추가합니다.
    모든 플레이어의 뷰포트를 덮으므로, 일시정지 메뉴 등 모든 로컬 플레이어에게 영향을 주는 UI에 적합합니다.
  • Add to Player Screen: 특정 플레이어의 뷰포트에만 위젯을 추가합니다.
    다른 플레이어의 뷰포트에는 영향을 주지 않으므로, 각 플레이어별 HUD 등 개별 UI에 적합합니다.

Game Layer Manager

Game Layer Manager는 뷰포트의 지오메트리(Geometry) 정보를 가져오고, 이 위젯을 사용하는 로컬 플레이어를 얻으며, 위젯 레이어를 추가(권장하지 않음, 내부 구조를 잘 아는 경우에만 사용)할 수 있는 최소한의 인터페이스입니다. 각 레이어는 해당 뷰포트에 추가된 모든 Slate 위젯을 보관하며, 실제 게임 뷰포트 Slate 위젯도 관리합니다.

Game Layer Manager의 구현은 SGameLayerManager.h/cpp에 위치해 있습니다. 여기에는 인터페이스(IGameLayerManager)와 실제 Compound Widget인 SGameLayerManager가 정의되어 있습니다. SGameLayerManager는 게임 뷰포트 위젯을 표시하는 역할을 하며, 인터페이스의 기본 구현 예시로도 참고할 수 있습니다.

이 위젯은 기본적으로 게임 뷰포트 클라이언트에서 가져온 단순한 사각형 레이아웃을 사용합니다. 또한 DPI 스케일 값의 변경을 감지하여 뷰포트와 모든 위젯 레이어의 스케일을 동적으로 조정하는 기능도 제공합니다.


Viewport Layout

기본적으로 뷰포트 레이아웃 디자인은 사각형 형태이며, 중심점의 X/Y 위치와 X/Y 크기를 0~1 비율로 지정하여 화면에 배치합니다.
이 설정은 기본 UGameViewportClient의 생성자에서 이루어집니다.
만약 커스텀 뷰포트 모양(예: 원형, 삼각형 등)을 만들고 싶다면, 직접 Game Layer Manager 클래스를 구현하여 원하는 형태로 계산하고 해당 뷰포트 위젯에 적용해야 합니다.

이 이미지는 게임 레이어 매니저가 어떻게 동작하는지, Slate Application이 이 시스템들과 어떻게 상호작용하는지 시각적으로 설명한 개요입니다.


Local Players

각 로컬 플레이어는 한 머신(PC/콘솔 등)에서 하나의 ULocalPlayer 객체로 표현되며, 이 객체는 레벨이 바뀌어도 계속 유지됩니다. 즉, UObject 타입으로서 플레이어를 직접적으로 나타내는 객체입니다.
각 로컬 플레이어는 자신이 어떤 게임패드(컨트롤러)를 사용하는지, 어떤 뷰포트 클라이언트를 사용하는지, 해당 Slate User를 얻는 방법, 온라인 서브시스템 정보 등을 파악하는 데 유용합니다.

로컬 플레이어는 애플리케이션의 다양한 요소에서 서로 다른 ID로 추적됩니다. 주요 ID는 다음과 같습니다:

  • 플랫폼 ID: 이 머신에서의 플레이어 인덱스(예: 0번이 최초 플레이어).
  • 컨트롤러 ID: 이름은 컨트롤러지만 실제로는 게임패드 ID를 의미합니다.
  • 고유 네트워크 ID(Unique Net ID): 온라인 멀티플레이어 환경에서 이 플레이어를 식별하는 고유 네트워크 ID입니다.

Gamepad ID(Controller ID)

Gamepad ID는 실제로 Controller ID를 의미하지만, 혼동을 피하기 위해 이 문서에서는 앞으로 Gamepad ID라는 용어를 사용합니다.
또한 Gamepad ID 관련 기능은 블루프린트에 노출되어 있지 않으므로 직접 C++에서 처리해야 하지만, 모두 ULocalPlayer의 public 값과 함수로 접근할 수 있습니다.

로컬 플레이어는 자신이 사용하는 Gamepad ID를 ULocalPlayer::GetControllerId()로 확인할 수 있습니다.
다른 Gamepad로 변경하려면 ULocalPlayer::SetControllerId()를 호출하면 됩니다.
만약 이미 다른 플레이어가 해당 ID를 사용 중이라면, 강제로 두 플레이어의 Gamepad ID가 서로 교체되며, 플랫폼 유저 ID도 함께 갱신됩니다.
Gamepad ID가 변경될 때 호출되는 델리게이트는 ULocalPlayer::OnControllerIdChanged()로 받을 수 있습니다.

왜 Controller ID라는 이름을 쓸까?
솔직히 저도 이유를 모르겠습니다. Epic이 이 네이밍을 명확히 바꿔주길 바랍니다.
현재 이름은 Player Controller와 혼동될 수 있는데, 그냥 Gamepad라고 하면 오해가 없을 텐데 말이죠.
코드 주석에서도 physical ControllerID라고만 설명되어 있어, Player Controller와 혼동될 여지가 있습니다.

profile
언리얼 엔진 주니어 개발자 입니다.

0개의 댓글