A modified CommonLoadingScreen based on the plugin from the Lyra sample project.
This commit is contained in:
commit
cebe626ec4
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
Binaries/
|
||||
Intermediate/
|
||||
29
CommonLoadingScreen.uplugin
Normal file
29
CommonLoadingScreen.uplugin
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 1,
|
||||
"VersionName": "1.0",
|
||||
"FriendlyName": "CommonLoadingScreen",
|
||||
"Description": "Loading screen manager handling creation and display of a project-specified loading screen widget",
|
||||
"Category": "Gameplay",
|
||||
"CreatedBy": "Epic Games, Inc.",
|
||||
"CreatedByURL": "https://www.epicgames.com",
|
||||
"DocsURL": "",
|
||||
"MarketplaceURL": "",
|
||||
"SupportURL": "",
|
||||
"CanContainContent": false,
|
||||
"IsBetaVersion": false,
|
||||
"IsExperimentalVersion": false,
|
||||
"Installed": false,
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "CommonLoadingScreen",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "Default"
|
||||
},
|
||||
{
|
||||
"Name": "CommonStartupLoadingScreen",
|
||||
"Type": "ClientOnly",
|
||||
"LoadingPhase": "PreLoadingScreen"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Resources/Icon128.png
Normal file
BIN
Resources/Icon128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
57
Source/CommonLoadingScreen/CommonLoadingScreen.Build.cs
Normal file
57
Source/CommonLoadingScreen/CommonLoadingScreen.Build.cs
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class CommonLoadingScreen : ModuleRules
|
||||
{
|
||||
public CommonLoadingScreen(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add public include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add other private include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"InputCore",
|
||||
"PreLoadScreen",
|
||||
"RenderCore",
|
||||
"DeveloperSettings",
|
||||
"UMG"
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
// ... add any modules that your module loads dynamically here ...
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
IMPLEMENT_MODULE(FDefaultModuleImpl, CommonLoadingScreen)
|
||||
@ -0,0 +1,13 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "CommonLoadingScreenSettings.h"
|
||||
|
||||
#include "UObject/NameTypes.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(CommonLoadingScreenSettings)
|
||||
|
||||
UCommonLoadingScreenSettings::UCommonLoadingScreenSettings()
|
||||
{
|
||||
CategoryName = TEXT("Game");
|
||||
}
|
||||
|
||||
@ -0,0 +1,68 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/DeveloperSettingsBackedByCVars.h"
|
||||
#include "HAL/Platform.h"
|
||||
#include "UObject/SoftObjectPath.h"
|
||||
#include "UObject/UObjectGlobals.h"
|
||||
|
||||
#include "CommonLoadingScreenSettings.generated.h"
|
||||
|
||||
|
||||
/**
|
||||
* Settings for a loading screen system.
|
||||
*/
|
||||
UCLASS(config=Game, defaultconfig, meta=(DisplayName="Common Loading Screen"))
|
||||
class UCommonLoadingScreenSettings : public UDeveloperSettingsBackedByCVars
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UCommonLoadingScreenSettings();
|
||||
|
||||
public:
|
||||
|
||||
// The widget to load for the loading screen.
|
||||
UPROPERTY(config, EditAnywhere, Category=Display)
|
||||
TSoftClassPtr<class ULoadingScreenWidget> LoadingScreenWidget;
|
||||
|
||||
// The z-order of the loading screen widget in the viewport stack
|
||||
UPROPERTY(config, EditAnywhere, Category=Display)
|
||||
int32 LoadingScreenZOrder = 100000;
|
||||
|
||||
// How long to hold the loading screen up after other loading finishes (in seconds) to
|
||||
// try to give texture streaming a chance to avoid blurriness
|
||||
//
|
||||
// Note: This is not normally applied in the editor for iteration time, but can be
|
||||
// enabled via HoldLoadingScreenAdditionalSecsEvenInEditor
|
||||
UPROPERTY(config, EditAnywhere, Category=Configuration, meta=(ForceUnits=s, ConsoleVariable="CommonLoadingScreen.HoldLoadingScreenAdditionalSecs"))
|
||||
float HoldLoadingScreenAdditionalSecs = 0.0f;
|
||||
|
||||
// The interval in seconds beyond which the loading screen is considered permanently hung (if non-zero).
|
||||
UPROPERTY(config, EditAnywhere, Category=Configuration, meta=(ForceUnits=s))
|
||||
float LoadingScreenHeartbeatHangDuration = 0.0f;
|
||||
|
||||
// The interval in seconds between each log of what is keeping a loading screen up (if non-zero).
|
||||
UPROPERTY(config, EditAnywhere, Category=Configuration, meta=(ForceUnits=s))
|
||||
float LogLoadingScreenHeartbeatInterval = 5.0f;
|
||||
|
||||
// When true, the reason the loading screen is shown or hidden will be printed to the log every frame.
|
||||
UPROPERTY(Transient, EditAnywhere, Category=Debugging, meta=(ConsoleVariable="CommonLoadingScreen.LogLoadingScreenReasonEveryFrame"))
|
||||
bool LogLoadingScreenReasonEveryFrame = 0;
|
||||
|
||||
// Force the loading screen to be displayed (useful for debugging)
|
||||
UPROPERTY(Transient, EditAnywhere, Category=Debugging, meta=(ConsoleVariable="CommonLoadingScreen.AlwaysShow"))
|
||||
bool ForceLoadingScreenVisible = false;
|
||||
|
||||
// Should we apply the additional HoldLoadingScreenAdditionalSecs delay even in the editor
|
||||
// (useful when iterating on loading screens)
|
||||
UPROPERTY(Transient, EditAnywhere, Category=Debugging)
|
||||
bool HoldLoadingScreenAdditionalSecsEvenInEditor = false;
|
||||
|
||||
// Should we apply the additional HoldLoadingScreenAdditionalSecs delay even in the editor
|
||||
// (useful when iterating on loading screens)
|
||||
UPROPERTY(config, EditAnywhere, Category=Configuration)
|
||||
bool ForceTickLoadingScreenEvenInEditor = true;
|
||||
};
|
||||
|
||||
312
Source/CommonLoadingScreen/Private/LoadingScreenManager.cpp
Normal file
312
Source/CommonLoadingScreen/Private/LoadingScreenManager.cpp
Normal file
@ -0,0 +1,312 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "LoadingScreenManager.h"
|
||||
|
||||
#include "HAL/ThreadHeartBeat.h"
|
||||
#include "ProfilingDebugging/CsvProfiler.h"
|
||||
|
||||
#include "LoadingScreenWidget.h"
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "Engine/GameViewportClient.h"
|
||||
#include "Engine/World.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Engine/LocalPlayer.h"
|
||||
#include "GameFramework/GameStateBase.h"
|
||||
#include "GameFramework/WorldSettings.h"
|
||||
#include "Misc/ConfigCacheIni.h"
|
||||
|
||||
#include "Framework/Application/IInputProcessor.h"
|
||||
#include "Framework/Application/SlateApplication.h"
|
||||
|
||||
#include "PreLoadScreenManager.h"
|
||||
|
||||
#include "ShaderPipelineCache.h"
|
||||
#include "CommonLoadingScreenSettings.h"
|
||||
|
||||
//@TODO: Used as the placeholder widget in error cases, should probably create a wrapper that at least centers it/etc...
|
||||
#include "Widgets/Images/SThrobber.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(LoadingScreenManager)
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogLoadingScreen, Log, All);
|
||||
DEFINE_LOG_CATEGORY(LogLoadingScreen);
|
||||
|
||||
//@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)
|
||||
|
||||
// Profiling category for loading screens
|
||||
CSV_DEFINE_CATEGORY(LoadingScreen, true);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool ILoadingProcessInterface::ShouldShowLoadingScreen(UObject* TestObject, FString& OutReason)
|
||||
{
|
||||
if (TestObject != nullptr)
|
||||
{
|
||||
if (ILoadingProcessInterface* LoadObserver = Cast<ILoadingProcessInterface>(TestObject))
|
||||
{
|
||||
FString ObserverReason;
|
||||
if (LoadObserver->ShouldShowLoadingScreen(/*out*/ ObserverReason))
|
||||
{
|
||||
if (ensureMsgf(!ObserverReason.IsEmpty(), TEXT("%s failed to set a reason why it wants to show the loading screen"), *GetPathNameSafe(TestObject)))
|
||||
{
|
||||
OutReason = ObserverReason;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace LoadingScreenCVars
|
||||
{
|
||||
// CVars
|
||||
static float HoldLoadingScreenAdditionalSecs = 0.0f;
|
||||
static FAutoConsoleVariableRef CVarHoldLoadingScreenUpAtLeastThisLongInSecs(
|
||||
TEXT("CommonLoadingScreen.HoldLoadingScreenAdditionalSecs"),
|
||||
HoldLoadingScreenAdditionalSecs,
|
||||
TEXT("How long to hold the loading screen up after other loading finishes (in seconds) to try to give texture streaming a chance to avoid blurriness"),
|
||||
ECVF_Default | ECVF_Preview);
|
||||
|
||||
static bool LogLoadingScreenReasonEveryFrame = false;
|
||||
static FAutoConsoleVariableRef CVarLogLoadingScreenReasonEveryFrame(
|
||||
TEXT("CommonLoadingScreen.LogLoadingScreenReasonEveryFrame"),
|
||||
LogLoadingScreenReasonEveryFrame,
|
||||
TEXT("When true, the reason the loading screen is shown or hidden will be printed to the log every frame."),
|
||||
ECVF_Default);
|
||||
|
||||
static bool ForceLoadingScreenVisible = false;
|
||||
static FAutoConsoleVariableRef CVarForceLoadingScreenVisible(
|
||||
TEXT("CommonLoadingScreen.AlwaysShow"),
|
||||
ForceLoadingScreenVisible,
|
||||
TEXT("Force the loading screen to show."),
|
||||
ECVF_Default);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// FLoadingScreenInputPreProcessor
|
||||
|
||||
// Input processor to throw in when loading screen is shown
|
||||
// This will capture any inputs, so active menus under the loading screen will not interact
|
||||
class FLoadingScreenInputPreProcessor : public IInputProcessor
|
||||
{
|
||||
public:
|
||||
FLoadingScreenInputPreProcessor() { }
|
||||
virtual ~FLoadingScreenInputPreProcessor() { }
|
||||
|
||||
bool CanEatInput() const
|
||||
{
|
||||
return !GIsEditor;
|
||||
}
|
||||
|
||||
//~IInputProcess interface
|
||||
virtual void Tick(const float DeltaTime, FSlateApplication& SlateApp, TSharedRef<ICursor> Cursor) override { }
|
||||
|
||||
virtual bool HandleKeyDownEvent(FSlateApplication& SlateApp, const FKeyEvent& InKeyEvent) override { return this->CanEatInput(); }
|
||||
virtual bool HandleKeyUpEvent(FSlateApplication& SlateApp, const FKeyEvent& InKeyEvent) override { return this->CanEatInput(); }
|
||||
virtual bool HandleAnalogInputEvent(FSlateApplication& SlateApp, const FAnalogInputEvent& InAnalogInputEvent) override { return this->CanEatInput(); }
|
||||
virtual bool HandleMouseMoveEvent(FSlateApplication& SlateApp, const FPointerEvent& MouseEvent) override { return this->CanEatInput(); }
|
||||
virtual bool HandleMouseButtonDownEvent(FSlateApplication& SlateApp, const FPointerEvent& MouseEvent) override { return this->CanEatInput(); }
|
||||
virtual bool HandleMouseButtonUpEvent(FSlateApplication& SlateApp, const FPointerEvent& MouseEvent) override { return this->CanEatInput(); }
|
||||
virtual bool HandleMouseButtonDoubleClickEvent(FSlateApplication& SlateApp, const FPointerEvent& MouseEvent) override { return this->CanEatInput(); }
|
||||
virtual bool HandleMouseWheelOrGestureEvent(FSlateApplication& SlateApp, const FPointerEvent& InWheelEvent, const FPointerEvent* InGestureEvent) override { return this->CanEatInput(); }
|
||||
virtual bool HandleMotionDetectedEvent(FSlateApplication& SlateApp, const FMotionEvent& MotionEvent) override { return this->CanEatInput(); }
|
||||
//~End of IInputProcess interface
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// ULoadingScreenManager
|
||||
|
||||
|
||||
void ULoadingScreenManager::Initialize(FSubsystemCollectionBase &Collection)
|
||||
{
|
||||
//FCoreUObjectDelegates::PreLoadMapWithContext.AddUObject(this, &ThisClass::HandlePreLoadMap);
|
||||
//FCoreUObjectDelegates::PostLoadMapWithWorld.AddUObject(this, &ThisClass::HandlePostLoadMap);
|
||||
|
||||
const UGameInstance *LocalGameInstance = this->GetGameInstance();
|
||||
check(LocalGameInstance);
|
||||
}
|
||||
|
||||
void ULoadingScreenManager::Deinitialize()
|
||||
{
|
||||
this->StopBlockingInput();
|
||||
this->RemoveWidgetFromViewport();
|
||||
|
||||
//FCoreUObjectDelegates::PreLoadMap.RemoveAll(this);
|
||||
//FCoreUObjectDelegates::PostLoadMapWithWorld.RemoveAll(this);
|
||||
}
|
||||
|
||||
bool ULoadingScreenManager::ShouldCreateSubsystem(UObject *Outer) const
|
||||
{
|
||||
// Only clients have loading screens
|
||||
const UGameInstance *GameInstance = CastChecked<UGameInstance>(Outer);
|
||||
const bool bIsServerWorld = GameInstance->IsDedicatedServerInstance();
|
||||
return !bIsServerWorld;
|
||||
}
|
||||
|
||||
class ULoadingScreenWidget *ULoadingScreenManager::ShowLoadingScreen()
|
||||
{
|
||||
if (this->bCurrentlyShowingLoadingScreen)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Unable to show loading screen if the engine is still loading with its loading screen.
|
||||
if (FPreLoadScreenManager::Get() && FPreLoadScreenManager::Get()->HasActivePreLoadScreenType(EPreLoadScreenTypes::EngineLoadingScreen))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
this->TimeLoadingScreenShown = FPlatformTime::Seconds();
|
||||
|
||||
this->bCurrentlyShowingLoadingScreen = true;
|
||||
|
||||
CSV_EVENT(LoadingScreen, TEXT("Show"));
|
||||
|
||||
const UCommonLoadingScreenSettings *Settings = GetDefault<UCommonLoadingScreenSettings>();
|
||||
UGameInstance *LocalGameInstance = this->GetGameInstance();
|
||||
|
||||
// Eat input while the loading screen is displayed
|
||||
this->StartBlockingInput();
|
||||
|
||||
this->LoadingScreenVisibilityChanged.Broadcast(/*bIsVisible=*/ true);
|
||||
|
||||
// Create the loading screen widget
|
||||
TSubclassOf<ULoadingScreenWidget> LoadingScreenWidgetClass = Settings->LoadingScreenWidget.LoadSynchronous();
|
||||
if (this->LoadingScreenUMGWidget = CreateWidget<ULoadingScreenWidget>(LocalGameInstance, LoadingScreenWidgetClass, NAME_None))
|
||||
{
|
||||
this->LoadingScreenWidget = this->LoadingScreenUMGWidget->TakeWidget();
|
||||
this->LoadingScreenUMGWidget->OnLoadScreenClosed.BindUObject(this, &ULoadingScreenManager::RemoveLoadingScreen);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogLoadingScreen, Error, TEXT("Failed to load the loading screen widget %s, falling back to placeholder."), *Settings->LoadingScreenWidget.ToString());
|
||||
this->LoadingScreenWidget = SNew(SThrobber);
|
||||
}
|
||||
|
||||
// Add to the viewport at a high ZOrder to make sure it is on top of most things
|
||||
UGameViewportClient *GameViewportClient = LocalGameInstance->GetGameViewportClient();
|
||||
GameViewportClient->AddViewportWidgetContent(this->LoadingScreenWidget.ToSharedRef(), Settings->LoadingScreenZOrder);
|
||||
|
||||
this->ChangePerformanceSettings(/*bEnableLoadingScreen=*/ true);
|
||||
|
||||
if (!GIsEditor || Settings->ForceTickLoadingScreenEvenInEditor)
|
||||
{
|
||||
// Tick Slate to make sure the loading screen is displayed immediately
|
||||
FSlateApplication::Get().Tick();
|
||||
}
|
||||
|
||||
return this->LoadingScreenUMGWidget;
|
||||
}
|
||||
|
||||
void ULoadingScreenManager::HideLoadingScreen()
|
||||
{
|
||||
this->LoadingScreenUMGWidget->BeginFadeOut();
|
||||
}
|
||||
|
||||
void ULoadingScreenManager::RemoveLoadingScreen()
|
||||
{
|
||||
if (!this->bCurrentlyShowingLoadingScreen)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this->StopBlockingInput();
|
||||
this->RemoveWidgetFromViewport();
|
||||
this->ChangePerformanceSettings(/*bEnableLoadingScreen=*/ false);
|
||||
|
||||
this->LoadingScreenUMGWidget->OnLoadScreenOpened.Unbind();
|
||||
this->LoadingScreenUMGWidget->OnLoadScreenClosed.Unbind();
|
||||
|
||||
// Let observers know that the loading screen is done
|
||||
this->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);
|
||||
|
||||
this->bCurrentlyShowingLoadingScreen = false;
|
||||
}
|
||||
|
||||
void ULoadingScreenManager::RemoveWidgetFromViewport()
|
||||
{
|
||||
UGameInstance *LocalGameInstance = GetGameInstance();
|
||||
if (this->LoadingScreenWidget.IsValid())
|
||||
{
|
||||
if (UGameViewportClient *GameViewportClient = LocalGameInstance->GetGameViewportClient())
|
||||
{
|
||||
GameViewportClient->RemoveViewportWidgetContent(this->LoadingScreenWidget.ToSharedRef());
|
||||
}
|
||||
this->LoadingScreenWidget.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void ULoadingScreenManager::StartBlockingInput()
|
||||
{
|
||||
if (!this->InputPreProcessor.IsValid())
|
||||
{
|
||||
this->InputPreProcessor = MakeShareable<FLoadingScreenInputPreProcessor>(new FLoadingScreenInputPreProcessor());
|
||||
FSlateApplication::Get().RegisterInputPreProcessor(this->InputPreProcessor, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void ULoadingScreenManager::StopBlockingInput()
|
||||
{
|
||||
if (this->InputPreProcessor.IsValid())
|
||||
{
|
||||
FSlateApplication::Get().UnregisterInputPreProcessor(this->InputPreProcessor);
|
||||
this->InputPreProcessor.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void ULoadingScreenManager::ChangePerformanceSettings(bool bEnabingLoadingScreen)
|
||||
{
|
||||
UGameInstance *LocalGameInstance = this->GetGameInstance();
|
||||
UGameViewportClient *GameViewportClient = LocalGameInstance->GetGameViewportClient();
|
||||
|
||||
FShaderPipelineCache::SetBatchMode(bEnabingLoadingScreen ? FShaderPipelineCache::BatchMode::Fast : FShaderPipelineCache::BatchMode::Background);
|
||||
|
||||
// Don't bother drawing the 3D world while we're loading
|
||||
GameViewportClient->bDisableWorldRendering = bEnabingLoadingScreen;
|
||||
|
||||
// Make sure to prioritize streaming in levels if the loading screen is up
|
||||
if (UWorld* ViewportWorld = GameViewportClient->GetWorld())
|
||||
{
|
||||
if (AWorldSettings *WorldSettings = ViewportWorld->GetWorldSettings(false, false))
|
||||
{
|
||||
WorldSettings->bHighPriorityLoadingLocal = bEnabingLoadingScreen;
|
||||
}
|
||||
}
|
||||
|
||||
if (bEnabingLoadingScreen)
|
||||
{
|
||||
// Set a new hang detector timeout multiplier when the loading screen is visible.
|
||||
double HangDurationMultiplier;
|
||||
if (!GConfig || !GConfig->GetDouble(TEXT("Core.System"), TEXT("LoadingScreenHangDurationMultiplier"), /*out*/ HangDurationMultiplier, GEngineIni))
|
||||
{
|
||||
HangDurationMultiplier = 1.0;
|
||||
}
|
||||
FThreadHeartBeat::Get().SetDurationMultiplier(HangDurationMultiplier);
|
||||
|
||||
// Do not report hitches while the loading screen is up
|
||||
FGameThreadHitchHeartBeat::Get().SuspendHeartBeat();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Restore the hang detector timeout when we hide the loading screen
|
||||
FThreadHeartBeat::Get().SetDurationMultiplier(1.0);
|
||||
|
||||
// Resume reporting hitches now that the loading screen is down
|
||||
FGameThreadHitchHeartBeat::Get().ResumeHeartBeat();
|
||||
}
|
||||
}
|
||||
|
||||
18
Source/CommonLoadingScreen/Private/LoadingScreenWidget.cpp
Normal file
18
Source/CommonLoadingScreen/Private/LoadingScreenWidget.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
// ©2022 Batty Bovine Productions, LLC. All Rights Reserved.
|
||||
|
||||
#include "LoadingScreenWidget.h"
|
||||
|
||||
|
||||
void ULoadingScreenWidget::LoadScreenOpened()
|
||||
{
|
||||
#if !WITH_EDITOR
|
||||
this->OnLoadScreenOpened.Execute();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ULoadingScreenWidget::LoadScreenClosed()
|
||||
{
|
||||
#if !WITH_EDITOR
|
||||
this->OnLoadScreenClosed.Execute();
|
||||
#endif
|
||||
}
|
||||
30
Source/CommonLoadingScreen/Public/LoadingProcessInterface.h
Normal file
30
Source/CommonLoadingScreen/Public/LoadingProcessInterface.h
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/Interface.h"
|
||||
|
||||
#include "LoadingProcessInterface.generated.h"
|
||||
|
||||
/** Interface for things that might cause loading to happen which requires a loading screen to be displayed */
|
||||
UINTERFACE(BlueprintType)
|
||||
class COMMONLOADINGSCREEN_API ULoadingProcessInterface : public UInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
class COMMONLOADINGSCREEN_API ILoadingProcessInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Checks to see if this object implements the interface, and if so asks whether or not we should
|
||||
// be currently showing a loading screen
|
||||
static bool ShouldShowLoadingScreen(UObject* TestObject, FString& OutReason);
|
||||
|
||||
virtual bool ShouldShowLoadingScreen(FString& OutReason) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
93
Source/CommonLoadingScreen/Public/LoadingScreenManager.h
Normal file
93
Source/CommonLoadingScreen/Public/LoadingScreenManager.h
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "UObject/WeakInterfacePtr.h"
|
||||
#include "Subsystems/GameInstanceSubsystem.h"
|
||||
|
||||
#include "LoadingScreenManager.generated.h"
|
||||
|
||||
|
||||
/**
|
||||
* Handles showing/hiding the loading screen
|
||||
*/
|
||||
UCLASS()
|
||||
class COMMONLOADINGSCREEN_API ULoadingScreenManager : public UGameInstanceSubsystem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
//~USubsystem interface
|
||||
virtual void Initialize(FSubsystemCollectionBase &Collection) override;
|
||||
virtual void Deinitialize() override;
|
||||
virtual bool ShouldCreateSubsystem(UObject *Outer) const override;
|
||||
//~End of USubsystem interface
|
||||
|
||||
/** Shows the loading screen. Sets up the loading screen widget on the viewport. */
|
||||
class ULoadingScreenWidget *ShowLoadingScreen();
|
||||
|
||||
/** Hides the loading screen. The loading screen widget will begin to fade out. */
|
||||
void HideLoadingScreen();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category=LoadingScreen)
|
||||
FString GetDebugReasonForShowingOrHidingLoadingScreen() const
|
||||
{
|
||||
return this->DebugReasonForShowingOrHidingLoadingScreen;
|
||||
}
|
||||
|
||||
/** Returns True when the loading screen is currently being shown */
|
||||
bool GetLoadingScreenDisplayStatus() const
|
||||
{
|
||||
return this->bCurrentlyShowingLoadingScreen;
|
||||
}
|
||||
|
||||
/** Called when the loading screen visibility changes */
|
||||
DECLARE_MULTICAST_DELEGATE_OneParam(FOnLoadingScreenVisibilityChangedDelegate, bool);
|
||||
FORCEINLINE FOnLoadingScreenVisibilityChangedDelegate &OnLoadingScreenVisibilityChangedDelegate() { return LoadingScreenVisibilityChanged; }
|
||||
|
||||
private:
|
||||
/** Removes the loading screen. The loading screen widget will be destroyed. */
|
||||
void RemoveLoadingScreen();
|
||||
|
||||
/** Removes the widget from the viewport */
|
||||
void RemoveWidgetFromViewport();
|
||||
|
||||
/** Prevents input from being used in-game while the loading screen is visible */
|
||||
void StartBlockingInput();
|
||||
|
||||
/** Resumes in-game input, if blocking */
|
||||
void StopBlockingInput();
|
||||
|
||||
void ChangePerformanceSettings(bool bEnabingLoadingScreen);
|
||||
|
||||
private:
|
||||
/** Delegate broadcast when the loading screen visibility changes */
|
||||
FOnLoadingScreenVisibilityChangedDelegate LoadingScreenVisibilityChanged;
|
||||
|
||||
/** A reference to the loading screen widget we are displaying (if any) */
|
||||
TObjectPtr<ULoadingScreenWidget> LoadingScreenUMGWidget;
|
||||
|
||||
/** A reference to the loading screen widget we are displaying (if any) */
|
||||
TSharedPtr<SWidget> LoadingScreenWidget;
|
||||
|
||||
/** Input processor to eat all input while the loading screen is shown */
|
||||
TSharedPtr<IInputProcessor> InputPreProcessor;
|
||||
|
||||
/** The reason why the loading screen is up (or not) */
|
||||
FString DebugReasonForShowingOrHidingLoadingScreen;
|
||||
|
||||
/** The time when we started showing the loading screen */
|
||||
double TimeLoadingScreenShown = 0.0;
|
||||
|
||||
/** The time the loading screen most recently wanted to be dismissed (might still be up due to a min display duration requirement) **/
|
||||
double TimeLoadingScreenLastDismissed = -1.0;
|
||||
|
||||
/** The time until the next log for why the loading screen is still up */
|
||||
double TimeUntilNextLogHeartbeatSeconds = 0.0;
|
||||
|
||||
/** True when we are between PreLoadMap and PostLoadMap */
|
||||
bool bCurrentlyInLoadMap = false;
|
||||
|
||||
/** True when the loading screen is currently being shown */
|
||||
bool bCurrentlyShowingLoadingScreen = false;
|
||||
};
|
||||
32
Source/CommonLoadingScreen/Public/LoadingScreenWidget.h
Normal file
32
Source/CommonLoadingScreen/Public/LoadingScreenWidget.h
Normal file
@ -0,0 +1,32 @@
|
||||
// ©2022 Batty Bovine Productions, LLC. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
|
||||
#include "LoadingScreenWidget.generated.h"
|
||||
|
||||
|
||||
/**
|
||||
* Loading screen widget base. Uses delegates to indicate when the load screen
|
||||
* is fully obscuring the game screen, and when it has finished fading back out.
|
||||
*/
|
||||
UCLASS()
|
||||
class COMMONLOADINGSCREEN_API ULoadingScreenWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void LoadScreenOpened();
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void LoadScreenClosed();
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent)
|
||||
void BeginFadeOut();
|
||||
|
||||
DECLARE_DELEGATE(FOnLoadScreenEvent)
|
||||
FOnLoadScreenEvent OnLoadScreenOpened;
|
||||
FOnLoadScreenEvent OnLoadScreenClosed;
|
||||
};
|
||||
@ -0,0 +1,55 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class CommonStartupLoadingScreen : ModuleRules
|
||||
{
|
||||
public CommonStartupLoadingScreen(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add public include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add other private include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"MoviePlayer",
|
||||
"PreLoadScreen",
|
||||
"DeveloperSettings"
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
// ... add any modules that your module loads dynamically here ...
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "CommonPreLoadScreen.h"
|
||||
|
||||
#include "CoreGlobals.h"
|
||||
#include "Misc/App.h"
|
||||
#include "SCommonPreLoadingScreenWidget.h"
|
||||
#include "Widgets/DeclarativeSyntaxSupport.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "CommonPreLoadingScreen"
|
||||
|
||||
void FCommonPreLoadScreen::Init()
|
||||
{
|
||||
if (!GIsEditor && FApp::CanEverRender())
|
||||
{
|
||||
EngineLoadingWidget = SNew(SCommonPreLoadingScreenWidget);
|
||||
}
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@ -0,0 +1,22 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PreLoadScreen.h"
|
||||
#include "PreLoadScreenBase.h"
|
||||
#include "Templates/SharedPointer.h"
|
||||
|
||||
class SWidget;
|
||||
|
||||
class FCommonPreLoadScreen : public FPreLoadScreenBase
|
||||
{
|
||||
public:
|
||||
|
||||
/*** IPreLoadScreen Implementation ***/
|
||||
virtual void Init() override;
|
||||
virtual EPreLoadScreenTypes GetPreLoadScreenType() const override { return EPreLoadScreenTypes::EngineLoadingScreen; }
|
||||
virtual TSharedPtr<SWidget> GetWidget() override { return EngineLoadingWidget; }
|
||||
private:
|
||||
|
||||
TSharedPtr<SWidget> EngineLoadingWidget;
|
||||
};
|
||||
@ -0,0 +1,67 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "CommonPreLoadScreen.h"
|
||||
#include "CoreGlobals.h"
|
||||
#include "Delegates/Delegate.h"
|
||||
#include "Misc/App.h"
|
||||
#include "Misc/CoreMisc.h"
|
||||
#include "Modules/ModuleInterface.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
#include "PreLoadScreenManager.h"
|
||||
#include "Templates/SharedPointer.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FCommonLoadingScreenModule"
|
||||
|
||||
class FCommonStartupLoadingScreenModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
bool IsGameModule() const override;
|
||||
|
||||
private:
|
||||
void OnPreLoadScreenManagerCleanUp();
|
||||
|
||||
TSharedPtr<FCommonPreLoadScreen> PreLoadingScreen;
|
||||
};
|
||||
|
||||
|
||||
void FCommonStartupLoadingScreenModule::StartupModule()
|
||||
{
|
||||
// No need to load these assets on dedicated servers.
|
||||
// Still want to load them in commandlets so cook catches them
|
||||
if (!IsRunningDedicatedServer())
|
||||
{
|
||||
PreLoadingScreen = MakeShared<FCommonPreLoadScreen>();
|
||||
PreLoadingScreen->Init();
|
||||
|
||||
if (!GIsEditor && FApp::CanEverRender() && FPreLoadScreenManager::Get())
|
||||
{
|
||||
FPreLoadScreenManager::Get()->RegisterPreLoadScreen(PreLoadingScreen);
|
||||
FPreLoadScreenManager::Get()->OnPreLoadScreenManagerCleanUp.AddRaw(this, &FCommonStartupLoadingScreenModule::OnPreLoadScreenManagerCleanUp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FCommonStartupLoadingScreenModule::OnPreLoadScreenManagerCleanUp()
|
||||
{
|
||||
//Once the PreLoadScreenManager is cleaning up, we can get rid of all our resources too
|
||||
PreLoadingScreen.Reset();
|
||||
ShutdownModule();
|
||||
}
|
||||
|
||||
void FCommonStartupLoadingScreenModule::ShutdownModule()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool FCommonStartupLoadingScreenModule::IsGameModule() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FCommonStartupLoadingScreenModule, CommonStartupLoadingScreen)
|
||||
@ -0,0 +1,40 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "SCommonPreLoadingScreenWidget.h"
|
||||
|
||||
#include "HAL/Platform.h"
|
||||
#include "Layout/Children.h"
|
||||
#include "Layout/Margin.h"
|
||||
#include "Math/Color.h"
|
||||
#include "Misc/Attribute.h"
|
||||
#include "Styling/CoreStyle.h"
|
||||
#include "Styling/ISlateStyle.h"
|
||||
#include "Styling/SlateColor.h"
|
||||
#include "Widgets/Layout/SBorder.h"
|
||||
|
||||
class FReferenceCollector;
|
||||
|
||||
#define LOCTEXT_NAMESPACE "SCommonPreLoadingScreenWidget"
|
||||
|
||||
void SCommonPreLoadingScreenWidget::Construct(const FArguments& InArgs)
|
||||
{
|
||||
ChildSlot
|
||||
[
|
||||
SNew(SBorder)
|
||||
.BorderImage(FCoreStyle::Get().GetBrush("WhiteBrush"))
|
||||
.BorderBackgroundColor(FLinearColor::Black)
|
||||
.Padding(0)
|
||||
];
|
||||
}
|
||||
|
||||
void SCommonPreLoadingScreenWidget::AddReferencedObjects(FReferenceCollector& Collector)
|
||||
{
|
||||
//WidgetAssets.AddReferencedObjects(Collector);
|
||||
}
|
||||
|
||||
FString SCommonPreLoadingScreenWidget::GetReferencerName() const
|
||||
{
|
||||
return TEXT("SCommonPreLoadingScreenWidget");
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@ -0,0 +1,27 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Containers/UnrealString.h"
|
||||
#include "UObject/GCObject.h"
|
||||
#include "Widgets/DeclarativeSyntaxSupport.h"
|
||||
#include "Widgets/SCompoundWidget.h"
|
||||
|
||||
class FReferenceCollector;
|
||||
|
||||
class SCommonPreLoadingScreenWidget : public SCompoundWidget, public FGCObject
|
||||
{
|
||||
public:
|
||||
SLATE_BEGIN_ARGS(SCommonPreLoadingScreenWidget) {}
|
||||
SLATE_END_ARGS()
|
||||
|
||||
void Construct(const FArguments& InArgs);
|
||||
|
||||
//~ Begin FGCObject interface
|
||||
virtual void AddReferencedObjects(FReferenceCollector& Collector) override;
|
||||
virtual FString GetReferencerName() const override;
|
||||
//~ End FGCObject interface
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user