Files
BlueRoseNote/03-UnrealEngine/Gameplay/Lyra/UE5 Lyra学习笔记(7)—CommonLoadingScreen(UE5.5).md
2025-08-02 12:09:34 +08:00

222 lines
9.3 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Untitled
date: 2025-07-25 14:49:41
excerpt:
tags:
rating: ⭐
---
# 前言
社区文章:
- [虚幻杂记4 PreLoadScreen与_LoadingScreen_](https://zhuanlan.zhihu.com/p/608502007)
- [UE5 _Lyra_项目的解析与学习三加载屏幕与RootLayoutUI创建流程](https://zhuanlan.zhihu.com/p/18123648655)
- 其他相关
- [UE4 MoviePlayer](https://zhuanlan.zhihu.com/p/346492104)
# 相关CVar参数
- CommonLoadingScreen.AlwaysShow总是显示
- CommonLoadingScreen.LogLoadingScreenReasonEveryFrame
- CommonLoadingScreen.HoldLoadingScreenAdditionalSecs载入完成后的等待时间。
# Lyra引用逻辑
ULyraExperienceManagerComponent
PreLoadMapWithContext委托会在UEngine::LoadMap()中被调用。
# 相关逻辑
- CommonLoadingScreen常规LoadingScreen。
- ***ULoadingScreenManager***:核心逻辑。
- UCommonLoadingScreenSettings各种设置参数。
- ULoadingProcessTask继承ILoadingProcessInterface接口只需覆盖ShouldShowLoadingScreen()。
- CommonStartupLoadingScreen模块游戏启动时的LoadingScreen。
- FCommonStartupLoadingScreenModuleStartupModule()、FPreLoadScreenManager::OnPreLoadScreenManagerCleanUp()绑定OnPreLoadScreenManagerCleanUp()。
- SCommonPreLoadingScreenWidgetLoadingScreen Slate控件。
## ULoadingScreenManager(UGameInstanceSubsystem)
- Initialize()绑定PreLoadMapWithContext与PostLoadMapWithWorld委托。
- Deinitialize()移除BlockInput、移除Widget、移除委托、关闭Tickable。
- ShouldCreateSubsystem()覆盖接口函数对于非Server端都会加载LoadingScreen。
- FTickableGameObject
- Tick():调用**ShouldCreateSubsystem()**计算TimeUntilNextLogHeartbeatSeconds。
- GetTickableTickType()
- IsTickable()如果GameInstance有效且拥有游戏窗口就可以进行Tick。
- GetStatId()STATGROUP_Tickables
- GetTickableGameObjectWorld()GetGameInstance()->GetWorld();
- RegisterLoadingProcessor()用于坐车任务在ULoadingProcessTask::CreateLoadingScreenProcessTask()被调用。
- UnregisterLoadingProcessor()用于卸载任务在ULoadingProcessTask::Unregister()被调用。
- **HandlePreLoadMap()**修改bCurrentlyInLoadMap为true之后调用UpdateLoadingScreen()。
- **HandlePostLoadMap()**修改bCurrentlyInLoadMap为false。
- **UpdateLoadingScreen()**通过ShouldShowLoadingScreen()来判断是ShowLoadingScreen()还是HideLoadingScreen()。并且中间使用FThreadHeartBeat::Get().MonitorCheckpointStart() / MonitorCheckpointEnd()来检查线程心跳?
- ***CheckForAnyNeedToShowLoadingScreen()***
- **ShouldShowLoadingScreen**():主要通过***CheckForAnyNeedToShowLoadingScreen()*** 判断是否应该显示LoadingScreen。如果HoldLoadingScreenAdditionalSecs大于0且当前经过时间依然小于HoldLoadingScreenAdditionalSecs也会显示LoadingScreen。
- IsShowingInitialLoadingScreen()判断是否正在显示初始化LoadingScreen。
- [[#ShowLoadingScreen()]]
- [[#HideLoadingScreen()]]
- RemoveWidgetFromViewport()从Viewport上移除LoadingScreenWidget。
- StartBlockingInput()通过FSlateApplication::Get().RegisterInputPreProcessor(MakeShareable< FLoadingScreenInputPreProcessor >(new FLoadingScreenInputPreProcessor()))来Block所有输入。
- StopBlockingInput()FSlateApplication::Get().UnregisterInputPreProcessor()接触Block。
- [[#ChangePerformanceSettings()]]:修改性能相关设置。
- TODO
- @TODO: Why can GetLocalPlayers() have nullptr entries? Can it really?
- @TODO: Test with PIE mode set to simulate and decide how much (if any) loading screen action should occur
- @TODO: Allow other things implementing ILoadingProcessInterface besides GameState/PlayerController (and owned components) to register as interested parties
- @TODO: ChangeMusicSettings (either here or using the LoadingScreenVisibilityChanged delegate)
- @TODO: Studio analytics (FireEvent_PIEFinishedLoading / tracking PIE startup time for regressions, either here or using the LoadingScreenVisibilityChanged delegate)
### ShowLoadingScreen()
```c++
void ULoadingScreenManager::ShowLoadingScreen()
{
if (bCurrentlyShowingLoadingScreen)
{
return;
}
//在Engine载入阶段不会执行后续ShowLoadingScreen逻辑
if (FPreLoadScreenManager::Get() && FPreLoadScreenManager::Get()->HasActivePreLoadScreenType(EPreLoadScreenTypes::EngineLoadingScreen))
{
return;
}
TimeLoadingScreenShown = FPlatformTime::Seconds();
bCurrentlyShowingLoadingScreen = true;
CSV_EVENT(LoadingScreen, TEXT("Show"));
const UCommonLoadingScreenSettings* Settings = GetDefault<UCommonLoadingScreenSettings>();
if (IsShowingInitialLoadingScreen())//已经显示LoadingScreen
{
UE_LOG(LogLoadingScreen, Log, TEXT("Showing loading screen when 'IsShowingInitialLoadingScreen()' is true."));
UE_LOG(LogLoadingScreen, Log, TEXT("%s"), *DebugReasonForShowingOrHidingLoadingScreen);
}
else
{
UE_LOG(LogLoadingScreen, Log, TEXT("Showing loading screen when 'IsShowingInitialLoadingScreen()' is false."));
UE_LOG(LogLoadingScreen, Log, TEXT("%s"), *DebugReasonForShowingOrHidingLoadingScreen);
UGameInstance* LocalGameInstance = GetGameInstance();
// LoadingScreen显示时Block所有Input
StartBlockingInput();
LoadingScreenVisibilityChanged.Broadcast(/*bIsVisible=*/ true);
// 创建LoadingScreen Widget
TSubclassOf<UUserWidget> LoadingScreenWidgetClass = Settings->LoadingScreenWidget.TryLoadClass<UUserWidget>();
if (UUserWidget* UserWidget = UUserWidget::CreateWidgetInstance(*LocalGameInstance, LoadingScreenWidgetClass, NAME_None))
{
LoadingScreenWidget = UserWidget->TakeWidget();
}
else
{
UE_LOG(LogLoadingScreen, Error, TEXT("Failed to load the loading screen widget %s, falling back to placeholder."), *Settings->LoadingScreenWidget.ToString());
LoadingScreenWidget = SNew(SThrobber);
}
// Add to the viewport at a high ZOrder to make sure it is on top of most things
// 将LoadingScreenWidget显示到Viewport上。
UGameViewportClient* GameViewportClient = LocalGameInstance->GetGameViewportClient();
GameViewportClient->AddViewportWidgetContent(LoadingScreenWidget.ToSharedRef(), Settings->LoadingScreenZOrder);
ChangePerformanceSettings(/*bEnableLoadingScreen=*/ true);
if (!GIsEditor || Settings->ForceTickLoadingScreenEvenInEditor)
{
// Tick Slate to make sure the loading screen is displayed immediately
FSlateApplication::Get().Tick();
}
}
}
```
### HideLoadingScreen()
```c++
void ULoadingScreenManager::HideLoadingScreen()
{
if (!bCurrentlyShowingLoadingScreen)
{
return;
}
//取消Block Input
StopBlockingInput();
if (IsShowingInitialLoadingScreen())
{
UE_LOG(LogLoadingScreen, Log, TEXT("Hiding loading screen when 'IsShowingInitialLoadingScreen()' is true."));
UE_LOG(LogLoadingScreen, Log, TEXT("%s"), *DebugReasonForShowingOrHidingLoadingScreen);
}
else
{
UE_LOG(LogLoadingScreen, Log, TEXT("Hiding loading screen when 'IsShowingInitialLoadingScreen()' is false."));
UE_LOG(LogLoadingScreen, Log, TEXT("%s"), *DebugReasonForShowingOrHidingLoadingScreen);
UE_LOG(LogLoadingScreen, Log, TEXT("Garbage Collecting before dropping load screen"));
//更新垃圾回收之间的计时器,以便在下一次机会时运行垃圾回收。
GEngine->ForceGarbageCollection(true);
RemoveWidgetFromViewport();
ChangePerformanceSettings(/*bEnableLoadingScreen=*/ false);
// Let observers know that the loading screen is done
LoadingScreenVisibilityChanged.Broadcast(/*bIsVisible=*/ false);
}
CSV_EVENT(LoadingScreen, TEXT("Hide"));
const double LoadingScreenDuration = FPlatformTime::Seconds() - TimeLoadingScreenShown;
UE_LOG(LogLoadingScreen, Log, TEXT("LoadingScreen was visible for %.2fs"), LoadingScreenDuration);
bCurrentlyShowingLoadingScreen = false;
}
```
### ChangePerformanceSettings()
```c++
void ULoadingScreenManager::ChangePerformanceSettings(bool bEnabingLoadingScreen)
{
UGameInstance* LocalGameInstance = GetGameInstance();
UGameViewportClient* GameViewportClient = LocalGameInstance->GetGameViewportClient();
//设置Shader编译模式默认为后台编译显示LoadingScreen时切换成Fast模式。
FShaderPipelineCache::SetBatchMode(bEnabingLoadingScreen ? FShaderPipelineCache::BatchMode::Fast : FShaderPipelineCache::BatchMode::Background);
//在LoadingScreen显示阶段阶段关闭世界渲染。
GameViewportClient->bDisableWorldRendering = bEnabingLoadingScreen;
//如果加载界面显示,请确保优先级为流式传输。
if (UWorld* ViewportWorld = GameViewportClient->GetWorld())
{
if (AWorldSettings* WorldSettings = ViewportWorld->GetWorldSettings(false, false))
{
WorldSettings->bHighPriorityLoadingLocal = bEnabingLoadingScreen;
}
}
if (bEnabingLoadingScreen)
{
// 当加载屏幕可见时,设置新的挂起检测超时倍数
double HangDurationMultiplier;
if (!GConfig || !GConfig->GetDouble(TEXT("Core.System"), TEXT("LoadingScreenHangDurationMultiplier"), /*out*/ HangDurationMultiplier, GEngineIni))
{
HangDurationMultiplier = 1.0;
}
FThreadHeartBeat::Get().SetDurationMultiplier(HangDurationMultiplier);
// 在加载界面显示时,请勿报告任何问题。
FGameThreadHitchHeartBeat::Get().SuspendHeartBeat();
}
else
{
// Restore the hang detector timeout when we hide the loading screen
FThreadHeartBeat::Get().SetDurationMultiplier(1.0);
// 现在加载屏幕已关闭,简历报告出现故障。
FGameThreadHitchHeartBeat::Get().ResumeHeartBeat();
}
}
```
### FPreLoadScreenManager