TIL - CommonUI, UCommonActivatableWidget, ..WidgetStack, NativeOnActivated(), Seamless Travel

조범근·2026년 7월 24일

TIL

목록 보기
81/81

TIL

CommonUI

UCommonActivatableWidget

기본 UUserWidget도 여러 UI를 동시에 화면에 띄울 수 있다.

하지만 실제 게임 UI를 구현하려면 단순히 AddToViewport() / RemoveFromParent()만으로는 부족하다.

예를 들어 설정 화면 위에 Confirm Popup이 등장했다고 가정한다.

Settings
    ↓
Confirm Popup 등장

Settings      → 뒤에 존재
Confirm Popup → 현재 입력의 주도권을 가짐

이때 다음과 같은 것들을 직접 관리해야 한다.

  • 현재 어떤 UI가 입력을 받아야 하는가?
  • 뒤로가기 입력은 누가 처리해야 하는가?
  • 새로운 화면이 등장했을 때 기존 화면은 어떤 상태가 되어야 하는가?
  • 다시 이전 화면으로 돌아갔을 때 입력과 Focus는 어떻게 복구할 것인가?

CommonUI는 이런 UI 흐름을 관리하기 위해 Widget의 존재 여부와 활성 상태를 분리한다.

UCommonActivatableWidget은 파괴하지 않고도 활성/비활성 상태를 바꿀 수 있고, 뒤로가기와 입력 라우팅 같은 UI 흐름에 참여하도록 설계되어 있다. 단, 모든 Widget을 **UCommonActivatableWidget**으로 만들 필요는 없다.

UCommonActivatableWidgetStack 안에서는 좀 다르다.
이 스택은 맨 위 위젯만 표시되고 활성화되며, 나머지는 비활성화된다. 따라서 항상 "뒤의 화면은 보이는데 입력만 막힌다"라고 이해하면 안 된다. 여러 UI Layer를 겹쳐 구성하면 그런 연출을 만들 수 있고, Activatable 자체의 핵심은 존재 여부와 활성 상태를 분리하는 것이다.


NativeOnActivated() / NativeOnDeactivated()

이 두 함수는 위젯의 Activation 상태가 바뀔 때 호출된다.

호출 순서:

Push Settings
    ↓
Settings Activate
    ↓
NativeOnActivated()

Push Popup
    ↓
Settings Deactivate
Popup Activate

Settings::NativeOnDeactivated()
Popup::NativeOnActivated()

Travel

멀티플레이에서 Lobby에 모인 모든 플레이어를 다음 게임 맵으로 이동시키려면 서버가 맵 이동의 주체가 되어야 한다.

예를 들어 각 클라이언트가 개별적으로 OpenLevel() 등을 실행하는 방식으로 이동시키면 서버와 클라이언트의 World 이동이 제대로 통제되지 않을 수 있다.

이떄 Seamless Travel 사용

Non-Seamless Travel Vs. Seamless Travel

Non-Seamless Travel
Non-Seamless Travel에서는 클라이언트가 기존 서버와의 연결을 끊고 새로운 Map이 준비된 서버에 다시 연결하는 과정을 거친다. 네트워크 연결 관점에서 새로 연결하는 강한 전환, 반드시 모든 게임 데이터가 무조건 초기화되는건 아님

Seamless Travel
연결을 유지한 상태로 Transition Map을 거쳐 레벨을 이동한다.

Transition Map이 필요한 이유는 항상 하나의 World는 로드되어 있어야 하므로 Old Map을 New Map 로드 전에 바로 제거할 수 없고, 두 대형 맵을 동시에 메모리에 두는 것을 피하기 위해 작은 중간 맵을 사용한다.

기본적으로 Seamless Travel에서 유지 대상으로 다뤄지는 것은 서버 측 GameMode, 유효한 PlayerState를 가진 Controller들, PlayerController 등이며, 추가 Actor는 GameModeBase::GetSeamlessTravelActorList()나 로컬 PlayerController::GetSeamlessTravelActorList()를 통해 지정할 수 있다

  • 서버 측: 게임모드 객체, 플레이어컨트롤러 객체
    GameMode::GetSeamlessTravelActorList() 함수를 재정의해서 추가 가능.
  • 클라 측: 플레이어컨트롤러 객체
    PlayerController::GetSeamlessTravelActorList() 함수를 재정의해서 추가 가능.

여기서 주의해야할 점은 일부 객체는 Destroy -> SPawn을 다시하는 게 아니라 이어서 사용될 수 있다. 즉, BeginPlay()같은 함수가 호출 되지 않을 수도 있음 그래서

Seamless Travel 후 초기화 로직을 무조건 BeginPlay()재호출에 의존하면 안 된다.

Seamless Travel 전용으로 PostSeamlessTravel()이나 HandleSeamlessTravelPlayer() 같은 후 처리 지점도 제공됩니다.

Travel 관련 함수

  • UWorld::ServerTravel()
    - 서버가 새로운 World/Level로 이동한다.
    - 연결된 모든 Client가 서버를 따라 이동한다.
    - 멀티플레이 게임에서 서버가 플레이어들을 Map 간 이동시킬 때 사용하는 핵심 함수이다.
    - 내부적으로 서버는 연결된 Client들의 APlayerController::ClientTravel()을 호출한다.
  • APlayerController::ClientTravel()
    호출 위치에 따라 의미가 달라짐.
    특정 Client에게 새로운 Map으로 이동하도록 지시하며 현재 서버 연결을 유지할 수 있다.

0개의 댓글