Seamless Travel이 일어난 이후 무슨 일이 일어나는가?
우선 Seamless Travel이 일어날 때, 새로운 GameMode가 AGameMode의 subclass이고, PlayerController 또한 이전과 같다면 PlayerConrtoller는 변화가 일어나지 않고 그대로 넘어오게 된다.
만약 그렇지 않다면, GameMode(또는 GameModeBase)의 HandleSeamlessTravelPlayer() 내에서 새로운 PlayerController가 만들어진다.
Seamless Travel이 일어난 이후, PlayerController가 이전과 다르다면 일부 주요 정보만 받아오는 SwapPlayerController()가 일어나게 된다.
[ AGameMode.cpp : HandleSeamlessTravelPlayer(AController*& C) ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
void AGameMode::HandleSeamlessTravelPlayer(AController*& C)
{
UE_LOG(LogGameMode, Log, TEXT(">> GameMode::HandleSeamlessTravelPlayer: %s "), *C->GetName());
APlayerController* PC = Cast<APlayerController>(C);
UClass* PCClassToSpawn = GetPlayerControllerClassToSpawnForSeamlessTravel(PC);
if (PC && PC->GetClass() != PCClassToSpawn)
{
if (PC->Player != nullptr)
{
// We need to spawn a new PlayerController to replace the old one
APlayerController* const NewPC = SpawnPlayerControllerCommon(PC->IsLocalPlayerController() ? ROLE_SimulatedProxy : ROLE_AutonomousProxy, PC->GetFocalLocation(), PC->GetControlRotation(), PCClassToSpawn);
if (NewPC == nullptr)
{
UE_LOG(LogGameMode, Warning, TEXT("Failed to spawn new PlayerController for %s (old class %s)"), *PC->GetHumanReadableName(), *PC->GetClass()->GetName());
PC->Destroy();
return;
}
else
{
PC->SeamlessTravelTo(NewPC);
NewPC->SeamlessTravelFrom(PC);
SwapPlayerControllers(PC, NewPC);
PC = NewPC;
C = NewPC;
}
}
else
{
PC->Destroy();
}
}
else
{
// clear out data that was only for the previous game
C->PlayerState->Reset();
// create a new PlayerState and copy over info; this is necessary because the old GameMode may have used a different PlayerState class
APlayerState* OldPlayerState = C->PlayerState;
C->InitPlayerState();
OldPlayerState->SeamlessTravelTo(C->PlayerState);
// we don"t need the old PlayerState anymore
//@fixme: need a way to replace PlayerStates that doesn't cause incorrect "player left the game"/"player entered the game" messages
OldPlayerState->Destroy();
}
InitSeamlessTravelPlayer(C);
// Initialize hud and other player details, shared with PostLogin
GenericPlayerInitialization(C);
if (PC)
{
// This may spawn the player pawn if the game is in progress
HandleStartingNewPlayer(PC);
}
UE_LOG(LogGameMode, Log, TEXT("<< GameMode::HandleSeamlessTravelPlayer: %s"), *C->GetName());
}
|
cs |
주목해야할 것은 HandleSeamlessTravelPlayer()내의 GenericPlayerInitialization(C) 이다.
이 함수내에서 "Travel의 종류와 무관하게" Travel이 완료된 후, PlayerController의 HUD가 파괴되고 재성성되는데,
InitializeHUDForPlayer(PC) 내에서 PlayerController에 있는 ClientSetHUD() 함수를 최종적으로 호출하며 이루어진다.
그럼 Seamless Travel이후 새로운 Match를 위한 Widget 초기화작업은 어디서 해야할까?
엔진 배포버전을 사용하고 있다면, AGameMode의 HandleSeamlessTravelPlayer()가 virtual 이므로 이 함수를 override해서 필요한 작업을 ClientRPC로 실행하면된다.
엔진을 수정한다면, APlayerController의 ClientSetHUD내에서 SpawnActor<AHUD> 이후 Widget을 초기화하는 부분을 같이 묶는다면 RPC 호출 횟수를 줄이는걸 기대해볼 수 있을 것이다.
하지만 GenericPlayerInitialization()은 PostLogin()에서도 호출되는 함수이므로, 이 부분 또한 생각해줘야 할 것이다.
이 방법은 아직 시도해보진 않았다.
[ 번외 ]
Seamless Travel은 대략 위의 흐름으로 진행되는데, HandleSeamlessTravelPlayer()는 위 흐름이 다 끝난 이후에 호출된다.
그러니까 위 흐름에선 이미 SeamlessTravel이 완료되었으나, 이전의 HUD는 파괴되지 않은 상태라고 볼 수 있다.
그렇기 때문에 Seamless Travel이 완료된 직후(매우 이른 시간), 어떤 작업을 하고 싶을 때 Client측에서 APlayerController::NotifyLoadedWorld(), Server측에서 APlayerController::PostSeamlessTravel()를 override해서 사용할 수 있을 것이다.
전체적인 SeamlessTravel의 흐름은 World.cpp에 있는 FSeamlessTravelHandler::Tick() 함수내의 후반 부분을 보면 된다.
[ Reference : EpicGameJapan, UE4 Multiplayer Online Deep Dive ~ https://www.docswell.com/s/EpicGamesJapan/ZDJ18Z-ue4-multiplayer-online-deep-dive-traveling-ue4dd#p48 ]
'UE5 > Network' 카테고리의 다른 글
[UE5] Replication Graph Deep Dive (0) | 2024.08.29 |
---|---|
Multiplayer in UE : How to Understand Network Replication (0) | 2024.08.29 |
[UE5] Replication - Push Model (0) | 2024.07.31 |
Listen Server에서 DestroySession()에 관한 궁금증 (0) | 2024.07.05 |
[UE5] Controller in BeginPlay on Server (0) | 2024.07.03 |