World Partition Loading Screen

I am using world partition for a pretty large map. When I change level from the main menu to the game map using WP you can see a number of the non-spacially loaded assets in the map as it loads the rest of the scene, and then the rest of the world pops into existence.

This behaviour is obviously undesirable and I would like to hide it behind a loading screen. The trouble is, when I create a widget at begin play for the WP level it doesn’t cover up the issue until the rest of the world is loaded in any way. And using the old method of having sublevels that load in the map will the engine and the build every time.

Is there an easy way to keep the screen black or display a widget to cover up the loading in the background?

Thanks in advance!

2 Likes

Hello HDDigitalDesigns,

Have you found a solution for the problem? I have the same problem and don’t know how to solve it.

Thanks in advance for posting your solution if you found one :wink:

Do I have the same problem? May I know how you solved it?

Same issue, anyone was able to solve it?

Same issue, I have found 0 documentation on the subject.

I’ve switched my game map to World Partition and noticed this issue today.

Previously, my loading screen covered up nicely if I transition from regular level to another regular level, however, if I transition from regular level to World Partition level, there is a small window where the loading screen couldn’t detect any meaningful status to keep showing and thus hidden for a split second.

Below is the code I used for checking - mainly copied from Lyra project:

	// Start out with 'unknown' reason in case someone forgets to put a reason when changing this in the future.
	DebugReasonForShowingOrHidingLoadingScreen = TEXT("Reason for Showing/Hiding LoadingScreen is unknown!");

	const UGameInstance* LocalGameInstance = GetGameInstance();

	if (LoadingScreenCVars::ForceLoadingScreenVisible)
	{
		DebugReasonForShowingOrHidingLoadingScreen = FString(TEXT("CommonLoadingScreen.AlwaysShow is true"));
		return true;
	}

	const FWorldContext* Context = LocalGameInstance->GetWorldContext();
	if (Context == nullptr)
	{
		// We don't have a world context right now... better show a loading screen
		DebugReasonForShowingOrHidingLoadingScreen = FString(TEXT("The game instance has a null WorldContext"));
		return true;
	}

	UWorld* World = Context->World();
	if (World == nullptr)
	{
		DebugReasonForShowingOrHidingLoadingScreen = FString(TEXT("We have no world (FWorldContext's World() is null)"));
		return true;
	}

	AGameStateBase* GameState = World->GetGameState<AGameStateBase>();
	if (GameState == nullptr)
	{
		// The game state has not yet replicated.
		DebugReasonForShowingOrHidingLoadingScreen = FString(TEXT("GameState hasn't yet replicated (it's null)"));
		return true;
	}

	if (bCurrentlyInLoadMap)
	{
		// Show a loading screen if we are in LoadMap
		DebugReasonForShowingOrHidingLoadingScreen = FString(TEXT("bCurrentlyInLoadMap is true"));
		return true;
	}

	if (!Context->TravelURL.IsEmpty())
	{
		// Show a loading screen when pending travel
		DebugReasonForShowingOrHidingLoadingScreen = FString(TEXT("We have pending travel (the TravelURL is not empty)"));
		return true;
	}

	if (Context->PendingNetGame != nullptr)
	{
		// Connecting to another server
		DebugReasonForShowingOrHidingLoadingScreen = FString(TEXT("We are connecting to another server (PendingNetGame != nullptr)"));
		return true;
	}

	if (!World->HasBegunPlay())
	{
		DebugReasonForShowingOrHidingLoadingScreen = FString(TEXT("World hasn't begun play"));
		return true;
	}

	if (World->IsInSeamlessTravel())
	{
		// Show a loading screen during seamless travel
		DebugReasonForShowingOrHidingLoadingScreen = FString(TEXT("We are in seamless travel"));
		return true;
	}

	// Ask the game state if it needs a loading screen	
	if (ILoadingProcessInterface::ShouldShowLoadingScreen(GameState, /*out*/ DebugReasonForShowingOrHidingLoadingScreen, /*out*/ bLoadingScreenInstant))
	{
		return true;
	}

	// Ask any game state components if they need a loading screen
	for (UActorComponent* TestComponent : GameState->GetComponents())
	{
		if (ILoadingProcessInterface::ShouldShowLoadingScreen(TestComponent, /*out*/ DebugReasonForShowingOrHidingLoadingScreen, /*out*/ bLoadingScreenInstant))
		{
			return true;
		}
	}

	// 2023/09/29 myw ADD: Ask all the game mode components if they need a loading screen, ex: UFTExperienceManagerComponent
	AGameModeBase* GameMode = World->GetAuthGameMode<AGameModeBase>();
	if (GameMode)
	{
		for (UActorComponent* TestComponent : GameMode->GetComponents())
		{
			if (ILoadingProcessInterface::ShouldShowLoadingScreen(TestComponent, /*out*/ DebugReasonForShowingOrHidingLoadingScreen, /*out*/ bLoadingScreenInstant))
			{
				return true;
			}
		}
	}

Does anyone have ideas on how to solve or circumvent this issue?
Thank you!

I want to share a solution I came up with. It’s far from perfect, but it might be a useful starting point for someone.

The core idea is to preload assets related to a level. The process looks roughly like this: scan the contents of the target level > gather dependencies > load resources > launch the level.

However, this approach has its caveats:

  1. World Partition levels can include a huge number of assets and resources.
  • Possible solutions:
    • Split the level into smaller parts and stream the remaining content separately.
    • Use PrimaryAssetLabel to load a specific subset of assets, and let the default system handle the rest.
  1. Even after the level is loaded, streaming may still be ongoing, so you’ll need to additionally check the streaming status.

Here’s a link to the repository: https://github.com/Pavreally/LevelProgressTracker
It’s where I tried to implement this subsystem — maybe it’ll be helpful to someone or spark some ideas for a better solution.

1 Like

Unbelievable,two years past,this issue still exist.When loading wp map from an normal map,there are some blur images and non-spacially loaded assets ,very bad

If you construct the widget from the game mode or game instance you may be able to fix this problem. Not for sure on this in but could be worth a try.

You load the widget in the controller class. Controllers are persistent. You ALWAYS have one.