PART 6 - 강의 2/3

Server Travel

Seamless Travel과 맵 전환 시 데이터 유지

01

Travel 타입

Seamless vs Non-Seamless

비교 ┌────────────────────┬──────────────────────┬──────────────────────┐ │ │ Non-Seamless │ Seamless │ ├────────────────────┼──────────────────────┼──────────────────────┤ │ 연결 유지 │ X (재접속) │ O │ │ Actor 유지 │ X │ O (설정 시) │ │ 로딩 화면 │ 필수 │ 선택적 │ │ Transition Map │ 불필요 │ 권장 │ │ 구현 복잡도 │ 낮음 │ 높음 │ │ 사용 케이스 │ 로비-게임 전환 │ 레벨 간 이동 │ └────────────────────┴──────────────────────┴──────────────────────┘
C++ // Non-Seamless Travel (기본) GetWorld()->ServerTravel("/Game/Maps/GameMap"); // Seamless Travel GetWorld()->ServerTravel("/Game/Maps/NextLevel?listen", true); // bAbsolute = true // GameMode에서 Seamless 활성화 AMyGameMode::AMyGameMode() { bUseSeamlessTravel = true; }
02

Seamless Travel 구현

Actor 유지와 Transition Map

C++ // GameMode에서 유지할 Actor 지정 void AMyGameMode::GetSeamlessTravelActorList( bool bToTransition, TArray<AActor*>& ActorList) { Super::GetSeamlessTravelActorList(bToTransition, ActorList); // 게임 상태 유지 if (AMyGameState* GS = GetGameState<AMyGameState>()) { ActorList.Add(GS); } // 모든 PlayerController 유지 (기본 동작) for (FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It) { if (APlayerController* PC = It->Get()) { ActorList.AddUnique(PC); } } } // PlayerController에서 Pawn 유지 설정 void AMyPlayerController::GetSeamlessTravelActorList( bool bToTransition, TArray<AActor*>& ActorList) { Super::GetSeamlessTravelActorList(bToTransition, ActorList); // Pawn 유지 if (GetPawn()) { ActorList.Add(GetPawn()); } }
Transition Map

Seamless Travel 시 빈 Transition Map을 사용하면 구 맵 언로드와 신 맵 로드 사이에 Actor들이 임시로 보관됩니다. Project Settings에서 설정하세요.

03

Travel 간 데이터 유지

GameInstance 활용

C++ // GameInstance는 Travel을 통해 유지됨 UCLASS() class UMyGameInstance : public UGameInstance { GENERATED_BODY() public: // 플레이어 세션 데이터 UPROPERTY() TMap<FUniqueNetIdRepl, FPlayerPersistentData> PlayerDataMap; // Travel 전 데이터 저장 void SavePlayerData(APlayerController* PC) { if (!PC || !PC->PlayerState) return; FUniqueNetIdRepl PlayerId = PC->PlayerState->GetUniqueId(); FPlayerPersistentData& Data = PlayerDataMap.FindOrAdd(PlayerId); // 데이터 수집 if (AMyPlayerState* PS = Cast<AMyPlayerState>(PC->PlayerState)) { Data.Score = PS->Score; Data.Inventory = PS->Inventory; } } // Travel 후 데이터 복원 void RestorePlayerData(APlayerController* PC) { if (!PC || !PC->PlayerState) return; FUniqueNetIdRepl PlayerId = PC->PlayerState->GetUniqueId(); if (FPlayerPersistentData* Data = PlayerDataMap.Find(PlayerId)) { if (AMyPlayerState* PS = Cast<AMyPlayerState>(PC->PlayerState)) { PS->Score = Data->Score; PS->Inventory = Data->Inventory; } } } };
04

Travel 이벤트

Travel 전후 처리

C++ // GameMode에서 Travel 완료 후 처리 void AMyGameMode::PostSeamlessTravel() { Super::PostSeamlessTravel(); // 이전 맵에서 유지된 플레이어들 처리 for (FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It) { if (APlayerController* PC = It->Get()) { // 새 맵에서 스폰 위치 지정 AActor* StartSpot = FindPlayerStart(PC); if (APawn* Pawn = PC->GetPawn()) { Pawn->SetActorLocation(StartSpot->GetActorLocation()); } } } } // PlayerController에서 Travel 처리 void AMyPlayerController::PostSeamlessTravel() { Super::PostSeamlessTravel(); // UI 재설정 if (IsLocalController()) { CreateHUD(); } // 클라이언트에게 Travel 완료 알림 ClientTravelComplete(); } UFUNCTION(Client, Reliable) void AMyPlayerController::ClientTravelComplete() { // 로딩 화면 숨기기 등 if (UMyGameInstance* GI = GetGameInstance<UMyGameInstance>()) { GI->HideLoadingScreen(); } }
SUMMARY

핵심 요약

  • Seamless Travel - 연결 유지하며 맵 전환, bUseSeamlessTravel 활성화
  • GetSeamlessTravelActorList - Travel 시 유지할 Actor 지정
  • GameInstance - Travel을 통해 유지되는 글로벌 데이터 저장소
  • PostSeamlessTravel - Travel 완료 후 초기화 로직
PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: Seamless Travel 구현

bUseSeamlessTravel = true로 설정하고, GetSeamlessTravelActorList()를 오버라이드하여 맵 전환 시 유지할 Actor(PlayerState, 인벤토리)를 지정하세요. 2개의 맵 간 전환을 테스트하세요.

실습 2: Non-Seamless Travel 처리

ServerTravel()로 맵 전환 시 AGameModeBase::PostLogin()에서 플레이어 데이터를 복원하는 로직을 구현하세요. SaveGame 시스템을 사용하여 전환 전 데이터를 저장하세요.

심화 과제

TransitionMap을 사용한 로딩 화면이 있는 Seamless Travel 시스템을 구현하세요. PostSeamlessTravel()에서 월드 초기화를 수행하고, 모든 클라이언트가 로딩을 완료할 때까지 게임 시작을 지연하는 동기화 로직을 구현하세요.