A modified CommonLoadingScreen based on the plugin from the Lyra sample project.

This commit is contained in:
Jamie Greunbaum 2023-03-10 00:54:58 -05:00
commit cebe626ec4
18 changed files with 890 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
Binaries/
Intermediate/

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View 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 ...
}
);
}
}

View File

@ -0,0 +1,5 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "Modules/ModuleManager.h"
IMPLEMENT_MODULE(FDefaultModuleImpl, CommonLoadingScreen)

View File

@ -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");
}

View File

@ -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;
};

View 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();
}
}

View 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
}

View 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;
}
};

View 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;
};

View 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;
};

View File

@ -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 ...
}
);
}
}

View File

@ -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

View File

@ -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;
};

View File

@ -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)

View File

@ -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

View File

@ -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:
};