diff --git a/Source/LoadingScreen/Private/LoadingScreenModule.cpp b/Source/LoadingScreen/Private/LoadingScreenModule.cpp index fa72a55..c960095 100644 --- a/Source/LoadingScreen/Private/LoadingScreenModule.cpp +++ b/Source/LoadingScreen/Private/LoadingScreenModule.cpp @@ -23,7 +23,11 @@ class FLoadingScreenModule : public ILoadingScreenModule private: void HandlePrepareLoadingScreen(); - void BeginLoadingScreen(const FLoadingScreenDescription& ScreenDescription); + void HandleMovieClipFinished(const FString& FinishedClip); + + void BeginLoadingScreen(const FLoadingScreenDescription& ScreenDescription); + + TSharedPtr WidgetLoadingScreen; }; IMPLEMENT_MODULE(FLoadingScreenModule, LoadingScreen) @@ -43,15 +47,15 @@ void FLoadingScreenModule::StartupModule() { Ref.TryLoad(); } - for ( const FStringAssetReference& Ref : Settings->DefaultScreen.Images ) { Ref.TryLoad(); } if ( IsMoviePlayerEnabled() ) - { - GetMoviePlayer()->OnPrepareLoadingScreen().AddRaw(this, &FLoadingScreenModule::HandlePrepareLoadingScreen); + { + // Binds the delegate to auto fire the loading screen code when a level changes and when a movie finishes + GetMoviePlayer()->OnPrepareLoadingScreen().AddRaw(this, &FLoadingScreenModule::HandlePrepareLoadingScreen); } // Prepare the startup screen, the PrepareLoadingScreen callback won't be called @@ -64,6 +68,10 @@ void FLoadingScreenModule::ShutdownModule() { if ( !IsRunningDedicatedServer() ) { + if (WidgetLoadingScreen.IsValid()) + { + WidgetLoadingScreen.Reset(); + } GetMoviePlayer()->OnPrepareLoadingScreen().RemoveAll(this); } } @@ -74,8 +82,31 @@ void FLoadingScreenModule::HandlePrepareLoadingScreen() BeginLoadingScreen(Settings->DefaultScreen); } +void FLoadingScreenModule::HandleMovieClipFinished(const FString & FinishedClip) +{ + // If its not the last movie then try keep waiting + if (!GetMoviePlayer()->IsLastMovieInPlaylist()) + { + return; + } + + // Unbind the delegate so we're not firing this multiple times + GetMoviePlayer()->OnMovieClipFinished().RemoveAll(this); + + // Show the loading screen widget + if (WidgetLoadingScreen.IsValid()) + { + WidgetLoadingScreen->HandleMoviesFinishedPlaying(); + } +} + void FLoadingScreenModule::BeginLoadingScreen(const FLoadingScreenDescription& ScreenDescription) { + if (WidgetLoadingScreen.IsValid()) + { + WidgetLoadingScreen.Reset(); + } + FLoadingScreenAttributes LoadingScreen; LoadingScreen.MinimumLoadingScreenDisplayTime = ScreenDescription.MinimumLoadingScreenDisplayTime; LoadingScreen.bAutoCompleteWhenLoadingCompletes = ScreenDescription.bAutoCompleteWhenLoadingCompletes; @@ -83,12 +114,31 @@ void FLoadingScreenModule::BeginLoadingScreen(const FLoadingScreenDescription& S LoadingScreen.bWaitForManualStop = ScreenDescription.bWaitForManualStop; LoadingScreen.MoviePaths = ScreenDescription.MoviePaths; LoadingScreen.PlaybackType = ScreenDescription.PlaybackType; - - if ( ScreenDescription.bShowUIOverlay ) + + + // Create and store widget + WidgetLoadingScreen = SNew(SSimpleLoadingScreen, ScreenDescription) + .bShowThrobber(ScreenDescription.Throbber.bShowThrobber) + .ThrobberType(ScreenDescription.Throbber.ThrobberType) + ; + LoadingScreen.WidgetLoadingScreen = WidgetLoadingScreen; + + // Incase we have no movie paths, this will force it to show the loading screen anyway + if (LoadingScreen.MoviePaths.Num() == 0) { - LoadingScreen.WidgetLoadingScreen = SNew(SSimpleLoadingScreen, ScreenDescription); + // Forces the movie player to create a movie streamer to actually show the widget and such + LoadingScreen.MoviePaths.Add(""); } + // If we have movies to show, then setup what happens if we're supposed to show ui otherwise skip this + else + { + // Edgecase + GetMoviePlayer()->OnMovieClipFinished().RemoveAll(this); + + GetMoviePlayer()->OnMovieClipFinished().AddRaw(this, &FLoadingScreenModule::HandleMovieClipFinished); + } + // This happens last after everything has been prepared ahead of time GetMoviePlayer()->SetupLoadingScreen(LoadingScreen); } diff --git a/Source/LoadingScreen/Private/LoadingScreenSettings.cpp b/Source/LoadingScreen/Private/LoadingScreenSettings.cpp index 6cabe7f..2ca1c35 100644 --- a/Source/LoadingScreen/Private/LoadingScreenSettings.cpp +++ b/Source/LoadingScreen/Private/LoadingScreenSettings.cpp @@ -1,27 +1,76 @@ // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "LoadingScreenSettings.h" -#include "UObject/ConstructorHelpers.h" +#include "CoreStyle.h" #include "Engine/Font.h" +#include "UObject/ConstructorHelpers.h" #define LOCTEXT_NAMESPACE "LoadingScreen" -FLoadingScreenDescription::FLoadingScreenDescription() - : LoadingText(LOCTEXT("Loading", "LOADING")) +FLoadingScreenSlotPosition::FLoadingScreenSlotPosition() + : Anchors(0.5f) + , Offset(NoInit) + , Alignment(NoInit) +{ } + +FLoadingScreenSlotText::FLoadingScreenSlotText() + : bShouldShowText(true) + , TextJustification(ETextJustify::Center) + , WrapAt(1000.0f) + , TextColor(FSlateColor(FLinearColor::White)) { + if (!IsRunningDedicatedServer()) + { + static ConstructorHelpers::FObjectFinder RobotoFontObj(TEXT("/Engine/EngineFonts/Roboto")); + Font = FSlateFontInfo(RobotoFontObj.Object, 20, FName("Normal"));; + } +} + +FLoadingScreenText::FLoadingScreenText() + : SlotText(FLoadingScreenSlotText()) + , SlotPosition(FLoadingScreenSlotPosition()) +{ } + +FLoadingScreenThrobber::FLoadingScreenThrobber() + : bShowThrobber(true) + , ThrobberType(EThrobberLoadingType::TLT_Regular) + , bFlipThrobberAnimation(false) + , NumPiecesThrobber(6) + , ThrobberImage(*FCoreStyle::Get().GetBrush("Throbber.Chunk")) + , ThrobberSlotPosition(FLoadingScreenSlotPosition()) + , AnimateHorizontally(true) + , AnimateVertically(true) + , AnimateOpacity(true) + , ThrobberPeriod(0.75f) + , ThrobberRadius(16.0f) +{ } + +FLoadingScreenTips::FLoadingScreenTips() + : SlotText(FLoadingScreenSlotText()) + , SlotPosition(FLoadingScreenSlotPosition()) +{ } + +FLoadingScreenDescription::FLoadingScreenDescription() + : MinimumLoadingScreenDisplayTime(-1.0f) + , bAutoCompleteWhenLoadingCompletes(true) + , bMoviesAreSkippable(true) + , bWaitForManualStop(false) + , bShowUIOverlay(true) + , bShowUiAfterMovies(true) + , Throbber(FLoadingScreenThrobber()) + , LoadingScreenText(FLoadingScreenText()) + , LoadingScreenDescription(FLoadingScreenText()) + , LoadingScreenTips(FLoadingScreenTips()) + , bShowImagesAfterMovies(true) + , ImageStretch(EStretch::ScaleToFit) +{ + LoadingScreenText.Text = LOCTEXT("Loading", "LOADING"); } ULoadingScreenSettings::ULoadingScreenSettings(const FObjectInitializer& Initializer) : Super(Initializer) -{ - TipWrapAt = 1000.0f; +{ - if ( !IsRunningDedicatedServer() ) - { - static ConstructorHelpers::FObjectFinder RobotoFontObj(TEXT("/Engine/EngineFonts/Roboto")); - TipFont = FSlateFontInfo(RobotoFontObj.Object, 20, FName("Normal")); - LoadingFont = FSlateFontInfo(RobotoFontObj.Object, 32, FName("Bold")); - } } #undef LOCTEXT_NAMESPACE diff --git a/Source/LoadingScreen/Private/LoadingScreenSettings.h b/Source/LoadingScreen/Private/LoadingScreenSettings.h index ceb6936..fa2a73c 100644 --- a/Source/LoadingScreen/Private/LoadingScreenSettings.h +++ b/Source/LoadingScreen/Private/LoadingScreenSettings.h @@ -3,67 +3,259 @@ #pragma once #include "CoreMinimal.h" +#include "Anchors.h" #include "Fonts/SlateFontInfo.h" -#include "SScaleBox.h" #include "MoviePlayer.h" +#include "SScaleBox.h" +#include "TextLayout.h" #include "Engine/DeveloperSettings.h" #include "LoadingScreenSettings.generated.h" +UENUM(BlueprintType) +enum class EThrobberLoadingType : uint8 +{ + TLT_Regular UMETA(DisplayName = "Regular"), + TLT_Circular UMETA(DisplayName = "Circular") +}; + +USTRUCT(BlueprintType) +struct LOADINGSCREEN_API FLoadingScreenSlotPosition +{ + GENERATED_BODY() + +public: + + FLoadingScreenSlotPosition(); + + /** The anchor for the Widget + * 0-X = Left Side + * 1-X = Right Side + * 0-Y = Top Side + * 1-Y = Bottom Side + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Slot Position") + FAnchors Anchors; + + /** The offset for the Widget + * -X = Left + * +X = Right + * -Y = Up + * +Y = Down + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Slot Position") + FVector2D Offset; + + /** Alignment pivot point of the Widget with 0.5 being center of either axis + * 0-X = Left Side + * 1-X = Right Side + * 0-Y = Top Side + * 1-Y = Bottom Side + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Slot Position") + FVector2D Alignment; + +}; + +USTRUCT(BlueprintType) +struct LOADINGSCREEN_API FLoadingScreenSlotText +{ + GENERATED_BODY() + +public: + + FLoadingScreenSlotText(); + + /** Flag for showing the widget. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tips") + bool bShouldShowText; + + /** The justification of the text. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tips") + TEnumAsByte TextJustification; + + /** The size of the text before it's wrapped to the next line. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tips") + float WrapAt; + + /** The color to use for the text */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading Screen Text") + FSlateColor TextColor; + + /** The font to display for text. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tips") + FSlateFontInfo Font; +}; + +USTRUCT(BlueprintType) +struct LOADINGSCREEN_API FLoadingScreenText +{ + GENERATED_BODY() + +public: + + FLoadingScreenText(); + + /** Text Information related to the text */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading Screen Text") + FLoadingScreenSlotText SlotText; + + /** The slot position of the text */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading Screen Text") + FLoadingScreenSlotPosition SlotPosition; + + /** The text to display on the loading screen. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading Screen Text", meta = (MultiLine = "true")) + FText Text; + +}; + +USTRUCT(BlueprintType) +struct LOADINGSCREEN_API FLoadingScreenThrobber +{ + GENERATED_BODY() + +public: + + FLoadingScreenThrobber(); + + /** Flag for showing the loading throbber if true, false will not show any throbber. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Throbber", meta = (InlineEditConditionToggle)) + bool bShowThrobber; + + /** Decides which throbber type to show if true, false will not show any throbber. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Throbber", meta = (EditCondition = "bShowThrobber")) + EThrobberLoadingType ThrobberType; + + /** Should throbber animate in opposite direction? Works for both regular and circular throbber */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Throbber") + bool bFlipThrobberAnimation; + + /** The numbers of pieces in the throbber when it is shown */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Throbber", meta = (ClampMin = "1", ClampMax = "25")) + int NumPiecesThrobber; + + /** The image for each throbber piece */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Throbber") + FSlateBrush ThrobberImage; + + /** The slot position of the throbber */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Throbber") + FLoadingScreenSlotPosition ThrobberSlotPosition; + + /** Should the pieces animate horizontally? */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Regular") + bool AnimateHorizontally; + + /** Should the pieces animate vertically? */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Regular") + bool AnimateVertically; + + /** Should the pieces animate their opacity? */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Regular") + bool AnimateOpacity; + + /** The amount of time for a full circle(in seconds) */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Circular") + float ThrobberPeriod; + + /** The radius of the circle */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Circular") + float ThrobberRadius; + +}; + +USTRUCT(BlueprintType) +struct LOADINGSCREEN_API FLoadingScreenTips +{ + GENERATED_BODY() + +public: + + FLoadingScreenTips(); + + /** Text Information related to the tips text */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tips") + FLoadingScreenSlotText SlotText; + + /** The slot position of the tips */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tips") + FLoadingScreenSlotPosition SlotPosition; + + /** The tips to display on the load screen. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tips", meta =(MultiLine = "true")) + TArray Tips; + +}; + USTRUCT(BlueprintType) struct LOADINGSCREEN_API FLoadingScreenDescription { - GENERATED_USTRUCT_BODY() + GENERATED_BODY() + +public: FLoadingScreenDescription(); /** The minimum time that a loading screen should be opened for, -1 if there is no minimum time. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Loading) - float MinimumLoadingScreenDisplayTime = -1; - + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Screens|Loading") + float MinimumLoadingScreenDisplayTime; + /** If true, the loading screen will disappear as soon as loading is done. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Loading) - bool bAutoCompleteWhenLoadingCompletes = true; - + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Screens|Loading") + bool bAutoCompleteWhenLoadingCompletes; + /** If true, movies can be skipped by clicking the loading screen as long as loading is done. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Loading) - bool bMoviesAreSkippable = true; - + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Screens|Loading") + bool bMoviesAreSkippable; + /** If true, movie playback continues until Stop is called. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Loading) - bool bWaitForManualStop = false; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Screens|Loading") + bool bWaitForManualStop; /** Should we just play back, loop, etc. NOTE: if playback type is MT_LoadingLoop, then MoviePlayer will auto complete when in the last movie and load finishes regardless of bAutoCompleteWhenLoadingCompletes */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Loading) - TEnumAsByte PlaybackType; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Screens|Movies") + TEnumAsByte PlaybackType; /** The movie paths local to the game's Content/Movies/ directory without extension. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Movies) - TArray MoviePaths; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Screens|Movies") + TArray MoviePaths; - /** Should we show the images/tips/loading text? Generally you'll want to set this to false if you just want to show a movie. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Display) - bool bShowUIOverlay = true; + /** Should we show the throbber/loading text etc? Generally you'll want to set this to false if you just want to show a movie. This will render over any movie/background image. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Screens|UI") + bool bShowUIOverlay; - /** Text displayed beside the animated icon */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Display) - FText LoadingText; + /** Flag for showing UI after all movies have been played successfully if true. False will show during movies. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Screens|UI", meta = (EditCondition = "bShowUIOverlay")) + bool bShowUiAfterMovies; - /** The texture display while in the loading screen on top of the movie. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Images, meta=(AllowedClasses="Texture2D")) - TArray Images; + /** Throbber to display when loading, can show circular and regular throbber types */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Screens|UI|Throbber", meta = (EditCondition = "bShowUIOverlay")) + FLoadingScreenThrobber Throbber; - /** The scaling type to apply to images. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Images) - TEnumAsByte ImageStretch = EStretch::ScaleToFit; + /** Text to display to indicate that the game is loading */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Screens|UI|Loading Text", meta = (EditCondition = "bShowUIOverlay")) + FLoadingScreenText LoadingScreenText; + + /** Optional text to display as a description. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Screens|UI|Description", meta = (EditCondition = "bShowUIOverlay")) + FLoadingScreenText LoadingScreenDescription; + + /** Optional text to display that will randomly swap out with other tips if applicable */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Screens|UI|Tips", meta = (EditCondition = "bShowUIOverlay")) + FLoadingScreenTips LoadingScreenTips; - /** The background color to use */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Images) - FLinearColor BackgroundColor = FLinearColor::Black; + /** Flag for showing images after all movies have been played successfully if true. False will show during movies. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Screens|UI|Images") + bool bShowImagesAfterMovies; - /** The background color to use for tips */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Images) - FLinearColor TipBackgroundColor = FLinearColor(0, 0, 0, 0.75f); + /** The texture display while in the loading screen on top of the movie. Will render after and over any movies. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Screens|UI|Images", meta = (AllowedClasses = "Texture2D")) + TArray Images; + + /** The scaling type to apply to images. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Screens|UI|Images") + TEnumAsByte ImageStretch; }; /** @@ -72,31 +264,18 @@ struct LOADINGSCREEN_API FLoadingScreenDescription UCLASS(config=Game, defaultconfig, meta=(DisplayName="Loading Screen")) class LOADINGSCREEN_API ULoadingScreenSettings : public UDeveloperSettings { - GENERATED_UCLASS_BODY() + GENERATED_BODY() public: + ULoadingScreenSettings(const FObjectInitializer& Initializer); + /** The startup screen for the project. */ - UPROPERTY(config, EditAnywhere, Category=Screens) + UPROPERTY(config, EditAnywhere, Category = "Screens") FLoadingScreenDescription StartupScreen; /** The default load screen between maps. */ - UPROPERTY(config, EditAnywhere, Category=Screens) + UPROPERTY(config, EditAnywhere, Category = "Screens") FLoadingScreenDescription DefaultScreen; - /** The font to display the tips in. */ - UPROPERTY(config, EditAnywhere, BlueprintReadWrite, Category=Advice) - FSlateFontInfo TipFont; - - /** The font to display on loading. */ - UPROPERTY(config, EditAnywhere, BlueprintReadWrite, Category = Display) - FSlateFontInfo LoadingFont; - - /** The size of the tip before it's wrapped to the next line. */ - UPROPERTY(config, EditAnywhere, BlueprintReadWrite, Category=Advice) - float TipWrapAt; - - /** The tips to display on the load screen. */ - UPROPERTY(config, EditAnywhere, BlueprintReadWrite, Category=Advice, meta = (MultiLine = "true")) - TArray Tips; }; diff --git a/Source/LoadingScreen/Private/SSimpleLoadingScreen.cpp b/Source/LoadingScreen/Private/SSimpleLoadingScreen.cpp index b80a27c..d176e90 100644 --- a/Source/LoadingScreen/Private/SSimpleLoadingScreen.cpp +++ b/Source/LoadingScreen/Private/SSimpleLoadingScreen.cpp @@ -2,19 +2,23 @@ #include "SSimpleLoadingScreen.h" +#include "Slate/DeferredCleanupSlateBrush.h" +#include "SScaleBox.h" +#include "Widgets/Images/SImage.h" +#include "Widgets/Layout/SSpacer.h" #include "Widgets/SOverlay.h" #include "Widgets/SBoxPanel.h" -#include "Widgets/Layout/SScaleBox.h" -#include "Widgets/Layout/SSpacer.h" -#include "Widgets/Layout/SBorder.h" -#include "Widgets/Layout/SSafeZone.h" #include "Widgets/Layout/SDPIScaler.h" #include "Widgets/Text/STextBlock.h" -#include "Widgets/Images/SImage.h" -#include "Widgets/Images/SThrobber.h" +#include "Widgets/Layout/SBorder.h" +#include "Widgets/Layout/SConstraintCanvas.h" +#include "SSafeZone.h" +#include "SThrobber.h" +#include "SDPIScaler.h" +#include "SlateApplication.h" #include "Engine/Texture2D.h" +#include "Engine/Engine.h" #include "Engine/UserInterfaceSettings.h" -#include "Slate/DeferredCleanupSlateBrush.h" #define LOCTEXT_NAMESPACE "LoadingScreen" @@ -32,128 +36,228 @@ static float PointSizeToSlateUnits(float PointSize) } void SSimpleLoadingScreen::Construct(const FArguments& InArgs, const FLoadingScreenDescription& InScreenDescription) -{ - const ULoadingScreenSettings* Settings = GetDefault(); +{ + LastToolTipUpdate = -1.0f; - const FSlateFontInfo& TipFont = Settings->TipFont; - const FSlateFontInfo& LoadingFont = Settings->LoadingFont; + ScreenDescriptionInfo = InScreenDescription; + // Only show on construct if UI is true and we're not showing it after movies + const bool bShowUiOnConstruct = (ScreenDescriptionInfo.bShowUIOverlay && !ScreenDescriptionInfo.bShowUiAfterMovies); + bShowThrobber = InArgs._bShowThrobber; + ThrobberType = InArgs._ThrobberType; + + // Construct the root of this widget TSharedRef Root = SNew(SOverlay); // If there's an image defined - if ( InScreenDescription.Images.Num() > 0 ) + if (ScreenDescriptionInfo.Images.Num() > 0) { - const int32 ImageIndex = FMath::RandRange(0, InScreenDescription.Images.Num() - 1); - const FStringAssetReference& ImageAsset = InScreenDescription.Images[ImageIndex]; + // Construct a random image to use for the loading screen + const int32 ImageIndex = FMath::RandRange(0, ScreenDescriptionInfo.Images.Num() - 1); + const FStringAssetReference& ImageAsset = ScreenDescriptionInfo.Images[ImageIndex]; UObject* ImageObject = ImageAsset.TryLoad(); if ( UTexture2D* LoadingImage = Cast(ImageObject) ) { - LoadingScreenBrush = FDeferredCleanupSlateBrush::CreateBrush(LoadingImage); + FVector2D Size = FVector2D(LoadingImage->GetSizeX(), LoadingImage->GetSizeY()); + LoadingScreenBrush = FDeferredCleanupSlateBrush::CreateBrush(LoadingImage, Size); + //LoadingImage, Size, FName(*ImageAsset.ToString())) + // Adds a slot to the root then add that image to the widget, renders over the movie if supposed to + BackgroundImageWidget = SNew(SImage) + .Visibility(ScreenDescriptionInfo.bShowImagesAfterMovies ? EVisibility::Hidden : EVisibility::SelfHitTestInvisible) + .Image(LoadingScreenBrush->GetSlateBrush()); - Root->AddSlot() + Root->AddSlot(0) .HAlign(HAlign_Fill) - .VAlign(VAlign_Fill) + .VAlign(VAlign_Fill) [ - SNew(SBorder) - .HAlign(HAlign_Fill) - .VAlign(VAlign_Fill) - .BorderBackgroundColor(InScreenDescription.BackgroundColor) - .BorderImage(FCoreStyle::Get().GetBrush("WhiteBrush")) + SNew(SScaleBox) + .Stretch(ScreenDescriptionInfo.ImageStretch) [ - SNew(SScaleBox) - .Stretch(InScreenDescription.ImageStretch) - [ - SNew(SImage) - .Image(LoadingScreenBrush.IsValid() ? LoadingScreenBrush->GetSlateBrush() : nullptr) - ] + BackgroundImageWidget.ToSharedRef() ] ]; } - } + } - TSharedRef TipWidget = SNullWidget::NullWidget; - if ( Settings->Tips.Num() > 0 ) + // Handles creating the throbber widget { - const int32 TipIndex = FMath::RandRange(0, Settings->Tips.Num() - 1); + // Decides which throbber type to show + switch (ThrobberType) + { + case EThrobberLoadingType::TLT_Circular: + { + // Old comment information may be of use in the future + // Convert font size to pixels, pixel_size = point_size * resolution / 72, then half it to get radius + // (Settings->LoadingFont.Size * 96.0f / 72.0f) / 2.0f - TipWidget = SNew(STextBlock) - .WrapTextAt(Settings->TipWrapAt) - .Font(TipFont) - .Text(Settings->Tips[TipIndex]); + ThrobberWidget = SNew(SCircularThrobber) + .Radius(ScreenDescriptionInfo.Throbber.ThrobberRadius) + .Period(ScreenDescriptionInfo.Throbber.ThrobberPeriod) + .NumPieces(ScreenDescriptionInfo.Throbber.NumPiecesThrobber) + .PieceImage(&ScreenDescriptionInfo.Throbber.ThrobberImage); + break; + } + case EThrobberLoadingType::TLT_Regular: + default: + { + const int32 AnimationParams = (ScreenDescriptionInfo.Throbber.AnimateVertically ? SThrobber::Vertical : 0) | + (ScreenDescriptionInfo.Throbber.AnimateHorizontally ? SThrobber::Horizontal : 0) | + (ScreenDescriptionInfo.Throbber.AnimateOpacity ? SThrobber::Opacity : 0); + + const SThrobber::EAnimation Animation = static_cast(AnimationParams); + + ThrobberWidget = SNew(SThrobber) + .Animate(Animation) + .NumPieces(ScreenDescriptionInfo.Throbber.NumPiecesThrobber) + .PieceImage(&ScreenDescriptionInfo.Throbber.ThrobberImage); + break; + } + } + + // Handles flipping the widget if needed + ThrobberWidget->SetRenderTransformPivot(FVector2D(0.5f, 0.5f)); + const float ThrobberScale = (float)((ScreenDescriptionInfo.Throbber.bFlipThrobberAnimation) ? -1 : 1); + ThrobberWidget->SetRenderTransform(FSlateRenderTransform(FScale2D(ThrobberScale, 1.0f))); + ThrobberWidget->SetVisibility((bShowThrobber && bShowUiOnConstruct) ? EVisibility::SelfHitTestInvisible : EVisibility::Hidden); } - else + + // Handles creating the tip text widget + if (ScreenDescriptionInfo.LoadingScreenTips.Tips.Num() > 0) + { + CurrentToolTipIndex = FMath::RandRange(0, ScreenDescriptionInfo.LoadingScreenTips.Tips.Num() - 1); + + CurrentToolTipWidget = SNew(STextBlock) + .Visibility((ScreenDescriptionInfo.LoadingScreenTips.SlotText.bShouldShowText && bShowUiOnConstruct) + ? EVisibility::SelfHitTestInvisible : EVisibility::Hidden) + .WrapTextAt(ScreenDescriptionInfo.LoadingScreenTips.SlotText.WrapAt) + .Font(ScreenDescriptionInfo.LoadingScreenTips.SlotText.Font) + .ColorAndOpacity(ScreenDescriptionInfo.LoadingScreenTips.SlotText.TextColor) + .Justification(ScreenDescriptionInfo.LoadingScreenTips.SlotText.TextJustification) + .Text(ScreenDescriptionInfo.LoadingScreenTips.Tips[CurrentToolTipIndex]); + + LastToolTipUpdate = FSlateApplication::Get().GetCurrentTime(); + } + + // Construct Description Text { - // Need to use a spacer when being rendered on another thread, incrementing the SNullWidget will - // lead to shared ptr crashes. - TipWidget = SNew(SSpacer); + DescriptionTextWidget = SNew(STextBlock) + .Text(ScreenDescriptionInfo.LoadingScreenDescription.Text) + .Font(ScreenDescriptionInfo.LoadingScreenDescription.SlotText.Font) + .ColorAndOpacity(ScreenDescriptionInfo.LoadingScreenDescription.SlotText.TextColor) + .Justification(ScreenDescriptionInfo.LoadingScreenDescription.SlotText.TextJustification) + .WrapTextAt(ScreenDescriptionInfo.LoadingScreenDescription.SlotText.WrapAt) + .Visibility((ScreenDescriptionInfo.LoadingScreenDescription.SlotText.bShouldShowText && bShowUiOnConstruct) + ? EVisibility::SelfHitTestInvisible : EVisibility::Hidden); } + // Construct Loading text + { + LoadingTextWidget = SNew(STextBlock) + .Text(ScreenDescriptionInfo.LoadingScreenText.Text) + .Font(ScreenDescriptionInfo.LoadingScreenText.SlotText.Font) + .ColorAndOpacity(ScreenDescriptionInfo.LoadingScreenText.SlotText.TextColor) + .Justification(ScreenDescriptionInfo.LoadingScreenText.SlotText.TextJustification) + .Visibility((ScreenDescriptionInfo.LoadingScreenText.SlotText.bShouldShowText && bShowUiOnConstruct) + ? EVisibility::SelfHitTestInvisible : EVisibility::Hidden); + } + + // Adds a slot to the root of this widget to allow for other widgets to be added to it, renders over image/movie Root->AddSlot() .HAlign(HAlign_Fill) - .VAlign(VAlign_Bottom) + .VAlign(VAlign_Fill) [ - SNew(SBorder) - .HAlign(HAlign_Fill) - .VAlign(VAlign_Fill) - .BorderBackgroundColor(InScreenDescription.TipBackgroundColor) - .BorderImage(FCoreStyle::Get().GetBrush("WhiteBrush")) + SNew(SSafeZone) [ - SNew(SSafeZone) - .HAlign(HAlign_Fill) - .VAlign(VAlign_Bottom) - .IsTitleSafe(true) - [ - SNew(SDPIScaler) - .DPIScale(this, &SSimpleLoadingScreen::GetDPIScale) - [ - SNew(SHorizontalBox) - - + SHorizontalBox::Slot() - .Padding(FMargin(25, 0.0f, 0, 0)) - .VAlign(VAlign_Center) - .AutoWidth() - [ - SNew(SCircularThrobber) - .Radius(PointSizeToSlateUnits(LoadingFont.Size) / 2.0f) - ] - - + SHorizontalBox::Slot() - .Padding(FMargin(40.0f, 0.0f, 0, 0)) - .AutoWidth() - .VAlign(VAlign_Center) - [ - SNew(STextBlock) - .Text(InScreenDescription.LoadingText) - .Font(LoadingFont) - ] - - + SHorizontalBox::Slot() - .FillWidth(1) - .HAlign(HAlign_Fill) - [ - SNew(SSpacer) - .Size(FVector2D(1.0f, 1.0f)) - ] - - + SHorizontalBox::Slot() - .AutoWidth() - .HAlign(HAlign_Right) - .VAlign(VAlign_Center) - .Padding(FMargin(10.0f)) - [ - TipWidget - ] - ] - ] + SNew(SConstraintCanvas) + + // Adds the circular throbber to the canvas + + SConstraintCanvas::Slot() + .Anchors(ScreenDescriptionInfo.Throbber.ThrobberSlotPosition.Anchors) + .Offset(FMargin(ScreenDescriptionInfo.Throbber.ThrobberSlotPosition.Offset)) + .Alignment(ScreenDescriptionInfo.Throbber.ThrobberSlotPosition.Alignment) + .AutoSize(true) + .ZOrder(1) + [ + ThrobberWidget.ToSharedRef() + ] + + // Adds the tip text to the canvas + + SConstraintCanvas::Slot() + .Anchors(ScreenDescriptionInfo.LoadingScreenTips.SlotPosition.Anchors) + .Offset(FMargin(ScreenDescriptionInfo.LoadingScreenTips.SlotPosition.Offset)) + .Alignment(ScreenDescriptionInfo.LoadingScreenTips.SlotPosition.Alignment) + .AutoSize(true) + .ZOrder(1) + [ + CurrentToolTipWidget.ToSharedRef() + ] + + // Adds the description text to the canvas + + SConstraintCanvas::Slot() + .Anchors(ScreenDescriptionInfo.LoadingScreenDescription.SlotPosition.Anchors) + .Offset(FMargin(ScreenDescriptionInfo.LoadingScreenDescription.SlotPosition.Offset)) + .Alignment(ScreenDescriptionInfo.LoadingScreenDescription.SlotPosition.Alignment) + .AutoSize(true) + .ZOrder(2) + [ + DescriptionTextWidget.ToSharedRef() ] - ]; + // Adds the loading text to the canvas + + SConstraintCanvas::Slot() + .Anchors(ScreenDescriptionInfo.LoadingScreenText.SlotPosition.Anchors) + .Offset(FMargin(ScreenDescriptionInfo.LoadingScreenText.SlotPosition.Offset)) + .Alignment(ScreenDescriptionInfo.LoadingScreenText.SlotPosition.Alignment) + .AutoSize(true) + .ZOrder(3) + [ + LoadingTextWidget.ToSharedRef() + ] + ] + ]; + ChildSlot [ Root ]; } +void SSimpleLoadingScreen::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) +{ + if ((InCurrentTime - LastToolTipUpdate) > 5.0f) + { + LastToolTipUpdate = InCurrentTime; + + // Safety check that it is a textblock and not something else + if (CurrentToolTipWidget->GetType() == "STextBlock") + { + STextBlock* TextBlock = static_cast(&CurrentToolTipWidget.ToSharedRef().Get()); + TextBlock->SetText(GetRandomToolTip()); + } + } +} + +void SSimpleLoadingScreen::HandleMoviesFinishedPlaying() +{ + // Show the background if we're allowed to + if (ScreenDescriptionInfo.bShowImagesAfterMovies) + { + BackgroundImageWidget->SetVisibility(EVisibility::SelfHitTestInvisible); + } + + // Show ui elements if we're showing ui and we can show it after movies + if (ScreenDescriptionInfo.bShowUiAfterMovies && ScreenDescriptionInfo.bShowUIOverlay) + { + ThrobberWidget->SetVisibility(ScreenDescriptionInfo.Throbber.bShowThrobber ? EVisibility::SelfHitTestInvisible : EVisibility::Hidden); + + LoadingTextWidget->SetVisibility(ScreenDescriptionInfo.LoadingScreenText.SlotText.bShouldShowText ? EVisibility::SelfHitTestInvisible : EVisibility::Hidden); + + DescriptionTextWidget->SetVisibility(ScreenDescriptionInfo.LoadingScreenDescription.SlotText.bShouldShowText ? EVisibility::SelfHitTestInvisible : EVisibility::Hidden); + + CurrentToolTipWidget->SetVisibility(ScreenDescriptionInfo.LoadingScreenTips.SlotText.bShouldShowText ? EVisibility::SelfHitTestInvisible : EVisibility::Hidden); + } +} + float SSimpleLoadingScreen::GetDPIScale() const { const FVector2D& DrawSize = GetCachedGeometry().ToPaintGeometry().GetLocalSize(); @@ -161,4 +265,38 @@ float SSimpleLoadingScreen::GetDPIScale() const return GetDefault()->GetDPIScaleBasedOnSize(Size); } +FText SSimpleLoadingScreen::GetRandomToolTip() +{ + + // Decides a random tip to show + { + const int32 Total = ScreenDescriptionInfo.LoadingScreenTips.Tips.Num(); + int32 RandomTip = FMath::RandRange(0, Total - 1); + // If there's only one then do nothing + if (Total == 1) + { + RandomTip = 0; + } + else + { + // Find a random index that is not currently used + while (RandomTip == CurrentToolTipIndex) + { + // Randomize the index + RandomTip = FMath::RandRange(0, Total - 1); + } + } + + // Update current index to be this randomized one + CurrentToolTipIndex = RandomTip; + } + + return ScreenDescriptionInfo.LoadingScreenTips.Tips[CurrentToolTipIndex]; +} + +bool SSimpleLoadingScreen::CanShowToolTip() const +{ + return (ScreenDescriptionInfo.LoadingScreenTips.Tips.Num() > 0); +} + #undef LOCTEXT_NAMESPACE diff --git a/Source/LoadingScreen/Private/SSimpleLoadingScreen.h b/Source/LoadingScreen/Private/SSimpleLoadingScreen.h index 91fab88..5493cab 100644 --- a/Source/LoadingScreen/Private/SSimpleLoadingScreen.h +++ b/Source/LoadingScreen/Private/SSimpleLoadingScreen.h @@ -1,8 +1,9 @@ -// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once #include "SCompoundWidget.h" +#include "CoreStyle.h" #include "LoadingScreenSettings.h" class FDeferredCleanupSlateBrush; @@ -11,15 +12,42 @@ class SSimpleLoadingScreen : public SCompoundWidget { public: - SLATE_BEGIN_ARGS(SSimpleLoadingScreen) {} - + SLATE_BEGIN_ARGS(SSimpleLoadingScreen) : + _bShowThrobber(false), + _ThrobberType(EThrobberLoadingType::TLT_Regular) + {} + SLATE_ARGUMENT(bool, bShowThrobber); + SLATE_ARGUMENT(EThrobberLoadingType, ThrobberType); SLATE_END_ARGS() void Construct(const FArguments& InArgs, const FLoadingScreenDescription& ScreenDescription); + virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override; + + /** Handles setting visibility on objects that should be showing after all loading movies finished showing */ + void HandleMoviesFinishedPlaying(); + private: float GetDPIScale() const; + + /** Returns a random tool tip that is not currently used and sets the current index to that new one */ + FText GetRandomToolTip(); + + /** Checks if we're able to show any tool tips */ + bool CanShowToolTip() const; -private: - TSharedPtr LoadingScreenBrush; + TSharedPtr LoadingScreenBrush; + + bool bShowThrobber; + EThrobberLoadingType ThrobberType; + + double LastToolTipUpdate; + int CurrentToolTipIndex; + TSharedPtr CurrentToolTipWidget = SNullWidget::NullWidget; + TSharedPtr LoadingTextWidget = SNullWidget::NullWidget; + TSharedPtr DescriptionTextWidget = SNullWidget::NullWidget; + TSharedPtr ThrobberWidget = SNullWidget::NullWidget; + TSharedPtr BackgroundImageWidget = SNullWidget::NullWidget; + + FLoadingScreenDescription ScreenDescriptionInfo; };