Rama’s C++ Game Startup Loading Screen Plugin
Download Link:
UE5.0 RamaCodeStartupLoadingScreen
~
UE5.1 RamaCodeStartupLoadingScreen
Supported Platforms: All
Precompiled Binaries: Windows
(This plugin will package fine along with other code for other platforms, but I am only providing precompiled binaries for Windows)
What am I offering to you, oh Lovely Community in this plugin:
- I am giving you a loading screen that will run ASAP when game is launched with editor closed (launched from commandline), standalone, or a packaged game. This is the UE5 version of the ShooterGame C++ Loading Screen module that needed some big changes to make it to UE5 land.
The Joy Of Customizeable Settings
- I am giving you lots of settings to play with to customize this C++ Loading Screen Module!
UPROPERTY(config, Category = "RamaCodeStartupLoadingScreenSettings", EditAnywhere, BlueprintReadWrite)
bool StartupLoadingScreenEnabled = true;
/**
This will make the Loading Screen work in a packaged build only, so you can run standalone / commandline editor builds without loading screen!
This ONLY WILL WORK IF StartupLoadingScreenEnabled = true!
*/
UPROPERTY(config, Category = "RamaCodeStartupLoadingScreenSettings", EditAnywhere, BlueprintReadWrite)
bool StartupLoadingScreenPackagedBuildsOnly = false;
/** Make sure to include this asset's folder in Additional Asset Folders to Cook if the asset is not being included in packaged game! <3 Rama */
UPROPERTY(config, Category = "RamaCodeStartupLoadingScreenSettings", EditAnywhere, BlueprintReadWrite)
FSoftObjectPath StartupLoadingImage;
UPROPERTY(config, Category = "RamaCodeStartupLoadingScreenSettings", EditAnywhere, BlueprintReadWrite)
FVector2D StartupLoadingScreenImageSize = FVector2D(1920,1080);
UPROPERTY(config, Category = "RamaCodeStartupLoadingScreenSettings", EditAnywhere, BlueprintReadWrite)
FLinearColor StartupLoadingScreenImageTint = FLinearColor::White;
//~~~ Progress Indicators ~~~
UPROPERTY(config, Category = "RamaCodeStartupLoadingScreenSettings", EditAnywhere, BlueprintReadWrite)
bool ProgressThrobberEnabled = true;
UPROPERTY(config, Category = "RamaCodeStartupLoadingScreenSettings", EditAnywhere, BlueprintReadWrite)
bool UseCircularProgressThrobber = true;
/** Default is Bottom Left */
UPROPERTY(config, Category = "RamaCodeStartupLoadingScreenSettings", EditAnywhere, BlueprintReadWrite)
bool ProgressThrobberPositionInBottomRight = false;
/** Padding relative to bottom left or right of screen */
UPROPERTY(config, Category = "RamaCodeStartupLoadingScreenSettings", EditAnywhere, BlueprintReadWrite)
float ProgressThrobberPadding = 15;
UPROPERTY(config, Category = "RamaCodeStartupLoadingScreenSettings", EditAnywhere, BlueprintReadWrite)
int32 ProgressThrobberPieceCount = 32;
UPROPERTY(config, Category = "RamaCodeStartupLoadingScreenSettings", EditAnywhere, BlueprintReadWrite)
float ProgressThrobberCircularPieceRadius = 16;
/** Speed of rotation! ♥ Rama */
UPROPERTY(config, Category = "RamaCodeStartupLoadingScreenSettings", EditAnywhere, BlueprintReadWrite)
float ProgressThrobberCircularPiecePeriodSeconds = 1;
-
The fastest way to test is to right click your uproject and select “Launch Game”, or run Standalone when editor is open.
-
I preload the Loading Screen Texture during cooking to make sure the asset gets included for packaging!
-
You can turn this plugin off any time, in settings, or have the loading screen only show up in packaged games, if you prefer!
-
Known Issue: In Editor builds, the texture shows up as grey-checkerbox for a few moments during startup, this does NOT occur in packaged builds!
How It Works
-
The Loading Screen image is stored as a FSoftObjPath, which is a string path ultimately, which means this can be saved as a config var.
-
This soft path / string asset reference is then dynamically loaded before the rest of your game, so that a loading screen image can be put up on the screen!
-
My class uses the fancy new UE PreLoadScreenManager to make this happen!
#include "PreLoadScreenBase.h"
#include "PreLoadScreenManager.h"
Progress Indicators? YES!!!
- I configured a pure C++ Slate widget with two different throbbers / progress bars that you can customize in the settings!
I Hope You Enjoy My Gift of Startup Loading Screens in UE5!
Rama
PS:
The Code
All of the code is in one file mostly, if you wanted to see it!
//Copyright For EverNewJoy Eternity and Beyond, And With Love & Joy & Harmony to You! ♥ Rama ♥
#include "Modules/ModuleInterface.h"
#include "Misc/App.h"
#include "GenericPlatform/GenericApplication.h"
#include "GenericPlatform/GenericApplicationMessageHandler.h"
#include "SlateBasics.h"
#include "SlateExtras.h"
#include "Engine/Texture2D.h"
//~~~
//THE FUTURE!!! ♥ Rama
#include "PreLoadScreenBase.h"
#include "PreLoadScreenManager.h"
//~~~
#include "RamaCodeStartupLoadingScreenSettings.h"
//~~~
struct FRamaCodeStartupLoadingScreenBrush : public FSlateDynamicImageBrush, public FGCObject
{
//GCObject allows for tracking this even if not UPROPERTY()
UTexture2D* TextureObj = nullptr;
FRamaCodeStartupLoadingScreenBrush( UTexture2D* T2D, const FVector2D& InImageSize, const FName& TexName, const FLinearColor& InTint )
: FSlateDynamicImageBrush( T2D, InImageSize, TexName, InTint )
{
TextureObj = T2D;
}
virtual void AddReferencedObjects(FReferenceCollector& Collector) override
{
if( TextureObj )
{
Collector.AddReferencedObject(TextureObj);
}
}
};
class SRamaCodeStartupLoadingScreenImage : public SCompoundWidget
{
public:
SLATE_BEGIN_ARGS(SRamaCodeStartupLoadingScreenImage) {}
SLATE_END_ARGS()
//~~~
void Construct(const FArguments& InArgs)
{
URamaCodeStartupLoadingScreenSettings* Settings = URamaCodeStartupLoadingScreenSettings::Get();
if(!Settings)
{
UE_LOG(LogTemp, Error, TEXT("URamaCodeStartupLoadingScreenSettings:: Settings not valid!"));
return;
}
if(!Settings->StartupLoadingImage.IsValid())
{
UE_LOG(LogTemp, Error, TEXT("URamaCodeStartupLoadingScreenSettings:: StartupLoadingImage was not set yet!"));
return;
}
const FString LoadingScreenName(Settings->StartupLoadingImage.ToString());
//This may show up as greybox for a few moments in Editor builds, works great in packaged games!
UTexture2D* TextureObj = LoadObject<UTexture2D>( NULL, *LoadingScreenName);
if(!TextureObj)
{
UE_LOG(LogTemp, Error, TEXT("URamaCodeStartupLoadingScreenSettings:: StartupLoadingImage Texture2D was not valid! <3 Rama"));
return;
}
//since we are not using game styles here, just load one image
LoadingScreenBrush = MakeShareable( new FRamaCodeStartupLoadingScreenBrush( TextureObj, Settings->StartupLoadingScreenImageSize, *LoadingScreenName, Settings->StartupLoadingScreenImageTint) );
ChildSlot
[
SNew(SOverlay)
+SOverlay::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
[
SNew(SImage)
.Image(LoadingScreenBrush.Get())
]
/*
SNew(SBorder)
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
.BorderImage(FCoreStyle::Get().GetBrush("WhiteBrush"))
.BorderBackgroundColor(FLinearColor::Yellow)
.Padding(0)
*/
/*
SNew(SOverlay)
*/
+SOverlay::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
[
SNew(SSafeZone)
.VAlign(VAlign_Bottom)
.HAlign( (Settings->ProgressThrobberPositionInBottomRight) ? HAlign_Right : HAlign_Left)
.Padding(15.0f)
.IsTitleSafe(true)
[
SNew(SCircularThrobber)
.NumPieces(Settings->ProgressThrobberPieceCount)
//SThrobber
//.Animate(SThrobber::EAnimation::All)
//.Animate(SThrobber::EAnimation::Vertical)
//SCircular
.Radius(Settings->ProgressThrobberCircularPieceRadius)
.Period(Settings->ProgressThrobberCircularPiecePeriodSeconds)
.Visibility( (Settings->UseCircularProgressThrobber) ? EVisibility::Visible : EVisibility::Collapsed)
]
]
+SOverlay::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
[
SNew(SSafeZone)
.VAlign(VAlign_Bottom)
.HAlign( (Settings->ProgressThrobberPositionInBottomRight) ? HAlign_Right : HAlign_Left)
.Padding(Settings->ProgressThrobberPadding)
.IsTitleSafe(true)
[
SNew(SThrobber)
.NumPieces(Settings->ProgressThrobberPieceCount)
//SThrobber
.Animate(SThrobber::EAnimation::All)
//.Animate(SThrobber::EAnimation::Vertical)
.Visibility( (Settings->UseCircularProgressThrobber) ? EVisibility::Collapsed : EVisibility::Visible )
]
]
];
}
private:
/** loading screen image brush */
TSharedPtr<FSlateDynamicImageBrush> LoadingScreenBrush;
};
//~~~
//See PreLoadScreenBase.h
class FJoyPreLoadScreen : public FPreLoadScreenBase
{
public:
/*** IPreLoadScreen Implementation ***/
virtual void Init() override
{
if (!GIsEditor && FApp::CanEverRender())
{
EngineLoadingWidget = SNew(SRamaCodeStartupLoadingScreenImage);
}
}
virtual EPreLoadScreenTypes GetPreLoadScreenType() const override { return EPreLoadScreenTypes::EngineLoadingScreen; }
virtual TSharedPtr<SWidget> GetWidget() override { return EngineLoadingWidget; }
private:
TSharedPtr<SWidget> EngineLoadingWidget;
};
class FRamaCodeStartupLoadingScreenModule : public IModuleInterface
{
public:
//Delegate
void OnPreLoadScreenManagerCleanUp()
{
//Once the PreLoadScreenManager is cleaning up, we can get rid of all our resources too
PreLoadingScreen.Reset();
//SHUTDOWN HERE
ShutdownModule();
}
TSharedPtr<FPreLoadScreenBase> PreLoadingScreen;
//~~~
//Startup
virtual void StartupModule() override
{
UE_LOG(LogTemp, Warning, TEXT("RamaCodeStartupLoadingScreen: Started! "));
URamaCodeStartupLoadingScreenSettings* Settings = URamaCodeStartupLoadingScreenSettings::Get();
if(!Settings)
{
UE_LOG(LogTemp, Error, TEXT("URamaCodeStartupLoadingScreenSettings:: Settings not valid!"));
return;
}
if(!Settings->StartupLoadingScreenEnabled)
{
UE_LOG(LogTemp, Warning, TEXT("URamaCodeStartupLoadingScreenSettings:: Exiting because not enabled in ProjectSettings! <3 Rama"));
return;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if(GIsEditor)
{
TestLoadingForObjectCooker();
return;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Packaged Builds Only?
if(Settings->StartupLoadingScreenPackagedBuildsOnly && WITH_EDITOR)
{
return;
}
//~~~
if (!IsRunningDedicatedServer())
{
if (!GIsEditor && FApp::CanEverRender() && FPreLoadScreenManager::Get())
{
PreLoadingScreen = MakeShared<FJoyPreLoadScreen>();
PreLoadingScreen->Init();
FPreLoadScreenManager::Get()->RegisterPreLoadScreen(PreLoadingScreen);
//PreloadScreenManager tells this module when to shutdown
FPreLoadScreenManager::Get()->OnPreLoadScreenManagerCleanUp.AddRaw(this, &FRamaCodeStartupLoadingScreenModule::OnPreLoadScreenManagerCleanUp);
}
}
//END OF STARTUP
}
void TestLoadingForObjectCooker()
{
//So user doesn't have to remember to manually add the asset in asset folders to package
// ♥ Rama
URamaCodeStartupLoadingScreenSettings* Settings = URamaCodeStartupLoadingScreenSettings::Get();
if(!Settings)
{
return;
}
if(!Settings->StartupLoadingImage.IsValid())
{
return;
}
//This will run during packaging and cause cooker to include the object because LoadObject was used.
UTexture2D* TextureObj = LoadObject<UTexture2D>( NULL, *Settings->StartupLoadingImage.ToString());
}
virtual bool IsGameModule() const override
{
return true;
}
};
IMPLEMENT_GAME_MODULE(FRamaCodeStartupLoadingScreenModule, RamaCodeStartupLoadingScreen);