보키_기록용
로딩화면에서 Throbber 화면 없애기 본문
내 로딩화면 중간에 Throbber가 떠있는 이상한 검은 화면이 중간에 떴다가 사라져서 이걸 삭제하기 위해 이것저것 알아봤다.
저 검은 화면은 무엇인가
알고보니 언리얼에서 기본으로 제공하는 로딩화면으로
SNew(SThrobber);
에 해당한다.
해당 Throbber를 생성하는 코드는 월드 파티션쪽에 속해있는데 아래 보다싶이 월드파티션되어있는 레벨을 로딩할때 GameMoviePlayer를 통해 로딩화면을 띄우지 않으면 기본 로딩화면을 띄운다.
void FStreamingPauseRenderingModule::BeginStreamingPause( FViewport* GameViewport )
{
// If a movie is already playing don't bother starting one
if(GetMoviePlayer()->IsInitialized() && !GetMoviePlayer()->IsMovieCurrentlyPlaying() && IsMoviePlayerEnabled())
{
TRACE_CPUPROFILER_EVENT_SCOPE(FStreamingPauseRenderingModule::BeginStreamingPause);
check(GameViewport);
//Create the viewport widget and add a throbber.
ViewportWidget = SNew(SViewport)
.EnableGammaCorrection(false);
ViewportWidget->SetContent
(
SNew(SVerticalBox)
+SVerticalBox::Slot()
.VAlign(VAlign_Bottom)
.HAlign(HAlign_Right)
.Padding(FMargin(10.0f))
[
SNew(SThrobber)
]
);
// . . .
}
}
시행착오 1 : 월드 파티션을 끄자
월드 파티션이 로딩되어 Throbber 화면이 뜨기까지의 과정은 먼저 Level.cpp에 OnLevelLoaded()부터 시작한다.
GetWorldPartition()에서 bIsPartitioned가 true면 월드파티션값을 가져온다.
void ULevel::OnLevelLoaded()
{
#if WITH_EDITOR
FixupActorFolders();
#endif
// Find associated level streaming for this level
const ULevelStreaming* LevelStreaming = FLevelUtils::FindStreamingLevel(this);
// Set level's associated WorldPartitionRuntimeCell for dynamically injected cells
if (LevelStreaming && !WorldPartitionRuntimeCell.GetUniqueID().IsValid())
{
WorldPartitionRuntimeCell = Cast<const UWorldPartitionRuntimeCell>(LevelStreaming->GetWorldPartitionCell());
}
// 1. Cook commandlet does it's own UWorldPartition::Initialize call in FWorldPartitionCookPackageSplitter::GetGenerateList
// 2. Do not Initialize if World doesn't have a UWorldPartitionSubsystem
if (!IsRunningCookCommandlet() && OwningWorld->HasSubsystem<UWorldPartitionSubsystem>())
{
if (UWorldPartition* WorldPartition = GetWorldPartition())
{
//
// When do we need to initialize the associated world partition object?
//
// - When the level is the main world persistent level
// - When the sublevel is streamed in the editor (mainly for data layers)
// - When the sublevel is streamed in game and the main world is not partitioned
//
const bool bIsOwningWorldGameWorld = OwningWorld->IsGameWorld();
const bool bIsOwningWorldPartitioned = OwningWorld->IsPartitionedWorld();
const bool bIsMainWorldLevel = OwningWorld->PersistentLevel == this;
const bool bInitializeForEditor = !bIsOwningWorldGameWorld;
const bool bInitializeForGame = bIsOwningWorldGameWorld;
UE_LOG(LogWorldPartition, Log, TEXT("ULevel::OnLevelLoaded(%s)(bIsOwningWorldGameWorld=%d, bIsOwningWorldPartitioned=%d, InitializeForMainWorld=%d, InitializeForEditor=%d, InitializeForGame=%d)"),
*GetTypedOuter<UWorld>()->GetName(), bIsOwningWorldGameWorld ? 1 : 0, bIsOwningWorldPartitioned ? 1 : 0, bIsMainWorldLevel ? 1 : 0, bInitializeForEditor ? 1 : 0, bInitializeForGame ? 1 : 0);
if (bIsMainWorldLevel || bInitializeForEditor)
{
if (!WorldPartition->IsInitialized())
{
FTransform Transform = LevelStreaming ? LevelStreaming->LevelTransform : FTransform::Identity;
WorldPartition->Initialize(OwningWorld, Transform);
}
}
}
}
}
bIsPartitioned
WorldPartition*이 있으면 Initialize를 해서 Delegate를 등록한다.
void UWorldPartition::RegisterDelegates()
{
// . . .
if (World->IsGameWorld())
{
if (IsMainWorldPartition())
{
World->OnWorldMatchStarting.AddUObject(this, &UWorldPartition::OnWorldMatchStarting);
#if !UE_BUILD_SHIPPING
FCoreDelegates::OnGetOnScreenMessages.AddUObject(this, &UWorldPartition::GetOnScreenMessages);
#endif
}
}
}
저 UWorldPartition::OnWorldMatchStarting()에서 BlockTillLevelStreamingCompleted()를 호출하는데, 아래 코드에서 BeginStreamingPauseDelegate의 Execute 되는 함수가 맨 위 코드의 BeginStreamingPause()이다.
void UWorld::BlockTillLevelStreamingCompleted()
{
// . . .
if (!bIsStreamingPaused && GEngine->GameViewport && GEngine->BeginStreamingPauseDelegate && GEngine->BeginStreamingPauseDelegate->IsBound())
{
GEngine->BeginStreamingPauseDelegate->Execute(GEngine->GameViewport->Viewport);
bIsStreamingPaused = true;
}
// . . .
}
과정은 알았는데, 이걸 해결하기 위해서 해당 레벨을 월드파티션을 끄는 방식이 빠를거같아서 먼저 시도해봤다.
언리얼 5.4.2 기준 스트리밍 활성화 버튼을 끄고 아래 Disable 버튼을 누르면 월드 파티션이 해제된다.
라고 생각했는데 해당 레벨 클래스 안에 bIsPartitioned 값은 여전히 1이었다! 처음부터 월드 파티션이 아닌 레벨은 Initialize를 잘 피해갔는데 이미 월드 파티션으로 만들어진 레벨은 월드 파티션을 비활성화한다고 해서 값이 0으로 바뀌진 않았다.
에셋레지스트리에 LevelIsPartitioned라는 태그로 아예 박혀있어서 그런가 쉽지않았다. 아예 새로운 레벨로 만들어서 적용해봤는데 여전히 안됨...
시행착오 2 : 언리얼이 하라는 대로 GameMoviePlayer를 쓰자
처음에 말했던 것처럼 저 Throbber 화면은 MoviePlayer가 실행되지 않았을 경우 실행되는 화면이다. 그렇다면 MoviePlayer를 써주자.
MoviePlayer 적용은 아래 링크를 보고 거의 똑같이 따라했다.
https://davidcio.me/blog/animated-loading-screen-ue5/
근데 기존 로딩 위젯보다 묘하게 확대된 듯이 표시되서 GetMoviePlayer()->SetupLoadingScreen(LoadingScreen); 전에 DPI 수정 코드를 추가했다.
FVector2D ViewportSize;
UGameViewportClient* GameViewportClient = GetGameInstance()->GetGameViewportClient();
if (GameViewportClient)
{
GameViewportClient->GetViewportSize(ViewportSize);
float DPIScale = GetDefault<UUserInterfaceSettings>()->GetDPIScaleBasedOnSize(FIntPoint(ViewportSize.X, ViewportSize.Y));
GetMoviePlayer()->SetViewportDPIScale(DPIScale);
GetMoviePlayer()->SetupLoadingScreen(LoadingScreen);
}
역시 하라는대로 하니깐 잘된다. 기존 작업자가 작업한거에 이어서 수정한거라 코드 파악부터 시행착오가 힘들었는데 잘되서 다행이다!
참고
'언리얼 > World Patitoin' 카테고리의 다른 글
데이터 레이어 사용하기 (0) | 2022.11.01 |
---|---|
월드 파티션이란? (0) | 2022.11.01 |