기본 UUserWidget도 여러 UI를 동시에 화면에 띄울 수 있다.
하지만 실제 게임 UI를 구현하려면 단순히 AddToViewport() / RemoveFromParent()만으로는 부족하다.
예를 들어 설정 화면 위에 Confirm Popup이 등장했다고 가정한다.
Settings
↓
Confirm Popup 등장
Settings → 뒤에 존재
Confirm Popup → 현재 입력의 주도권을 가짐
이때 다음과 같은 것들을 직접 관리해야 한다.
CommonUI는 이런 UI 흐름을 관리하기 위해 Widget의 존재 여부와 활성 상태를 분리한다.
UCommonActivatableWidget은 파괴하지 않고도 활성/비활성 상태를 바꿀 수 있고, 뒤로가기와 입력 라우팅 같은 UI 흐름에 참여하도록 설계되어 있다. 단, 모든 Widget을 **UCommonActivatableWidget**으로 만들 필요는 없다.
UCommonActivatableWidgetStack 안에서는 좀 다르다.
이 스택은 맨 위 위젯만 표시되고 활성화되며, 나머지는 비활성화된다. 따라서 항상 "뒤의 화면은 보이는데 입력만 막힌다"라고 이해하면 안 된다. 여러 UI Layer를 겹쳐 구성하면 그런 연출을 만들 수 있고, Activatable 자체의 핵심은 존재 여부와 활성 상태를 분리하는 것이다.
이 두 함수는 위젯의 Activation 상태가 바뀔 때 호출된다.
호출 순서:
Push Settings
↓
Settings Activate
↓
NativeOnActivated()
Push Popup
↓
Settings Deactivate
Popup Activate
Settings::NativeOnDeactivated()
Popup::NativeOnActivated()
멀티플레이에서 Lobby에 모인 모든 플레이어를 다음 게임 맵으로 이동시키려면 서버가 맵 이동의 주체가 되어야 한다.
예를 들어 각 클라이언트가 개별적으로 OpenLevel() 등을 실행하는 방식으로 이동시키면 서버와 클라이언트의 World 이동이 제대로 통제되지 않을 수 있다.
이떄 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()를 통해 지정할 수 있다
여기서 주의해야할 점은 일부 객체는 Destroy -> SPawn을 다시하는 게 아니라 이어서 사용될 수 있다. 즉, BeginPlay()같은 함수가 호출 되지 않을 수도 있음 그래서
Seamless Travel후 초기화 로직을 무조건BeginPlay()재호출에 의존하면 안 된다.
Seamless Travel 전용으로 PostSeamlessTravel()이나 HandleSeamlessTravelPlayer() 같은 후 처리 지점도 제공됩니다.
APlayerController::ClientTravel()을 호출한다.