Loading screen overlay + camera fade on sublevel load
- Add loading overlay (black background + "Loading..." text) behind UI panels - Overlay starts visible by default, hidden in NativeConstruct if no sublevel - CameraFade to black before sublevel load, with deferred fallback for BeginPlay - 2s delay after OnLevelShown before starting 2s fade-in from black - ShowLoadingScreen helper on PlayerController via HUD->MainWidget Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,9 @@
|
||||
#include "Engine/LevelStreamingDynamic.h"
|
||||
#include "Engine/PostProcessVolume.h"
|
||||
#include "PS_Editor_CameraStart.h"
|
||||
#include "PS_Editor_HUD.h"
|
||||
#include "PS_Editor_MainWidget.h"
|
||||
#include "Camera/PlayerCameraManager.h"
|
||||
#include "PS_Editor_GameInstanceSubsystem.h"
|
||||
#include "GameFramework/GameModeBase.h"
|
||||
#include "GameFramework/GameStateBase.h"
|
||||
@@ -136,6 +139,14 @@ void APS_Editor_PlayerController::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
// Deferred fade-to-black: CameraManager wasn't ready at BeginPlay
|
||||
if (bPendingFadeToBlack && PlayerCameraManager)
|
||||
{
|
||||
PlayerCameraManager->StartCameraFade(0.0f, 1.0f, 0.0f, FLinearColor::Black, true, true);
|
||||
bPendingFadeToBlack = false;
|
||||
ShowLoadingScreen(true);
|
||||
}
|
||||
|
||||
if (SpawnManager)
|
||||
{
|
||||
SpawnManager->TickEditorMode(DeltaTime);
|
||||
@@ -253,6 +264,17 @@ void APS_Editor_PlayerController::CancelPointPlacement()
|
||||
FinishPointPlacement();
|
||||
}
|
||||
|
||||
void APS_Editor_PlayerController::ShowLoadingScreen(bool bShow)
|
||||
{
|
||||
if (APS_Editor_HUD* EditorHUD = Cast<APS_Editor_HUD>(GetHUD()))
|
||||
{
|
||||
if (UPS_Editor_MainWidget* Widget = EditorHUD->GetMainWidget())
|
||||
{
|
||||
Widget->ShowLoadingScreen(bShow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APS_Editor_PlayerController::LoadBaseLevelAsSublevel(const FString& LevelName, const FString& MapPath)
|
||||
{
|
||||
// Unload previous sublevel first
|
||||
@@ -294,6 +316,19 @@ void APS_Editor_PlayerController::LoadBaseLevelAsSublevel(const FString& LevelNa
|
||||
return;
|
||||
}
|
||||
|
||||
// Fade to black + show loading overlay
|
||||
bWaitingForSublevelFadeIn = true;
|
||||
if (PlayerCameraManager)
|
||||
{
|
||||
PlayerCameraManager->StartCameraFade(0.0f, 1.0f, 0.0f, FLinearColor::Black, true, true);
|
||||
bPendingFadeToBlack = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
bPendingFadeToBlack = true;
|
||||
}
|
||||
ShowLoadingScreen(true);
|
||||
|
||||
// Use MapPath if provided, otherwise fall back to LevelName (legacy behavior)
|
||||
const FString& LevelToLoad = MapPath.IsEmpty() ? LevelName : MapPath;
|
||||
|
||||
@@ -362,6 +397,21 @@ void APS_Editor_PlayerController::DisableSublevelPostProcessMaterials()
|
||||
*CameraStart->GetActorLocation().ToString());
|
||||
}
|
||||
}
|
||||
|
||||
// Delay 2s then fade in from black over 2 seconds
|
||||
if (bWaitingForSublevelFadeIn)
|
||||
{
|
||||
bWaitingForSublevelFadeIn = false;
|
||||
FTimerHandle FadeTimer;
|
||||
GetWorldTimerManager().SetTimer(FadeTimer, [this]()
|
||||
{
|
||||
ShowLoadingScreen(false);
|
||||
if (PlayerCameraManager)
|
||||
{
|
||||
PlayerCameraManager->StartCameraFade(1.0f, 0.0f, 2.0f, FLinearColor::Black, false, false);
|
||||
}
|
||||
}, 2.0f, false);
|
||||
}
|
||||
}
|
||||
|
||||
void APS_Editor_PlayerController::HandleEditorClick(FVector2D ScreenPos, bool bAdditive)
|
||||
|
||||
@@ -136,9 +136,18 @@ private:
|
||||
UPROPERTY()
|
||||
TObjectPtr<ULevelStreamingDynamic> CurrentSublevel;
|
||||
|
||||
/** True when a sublevel is loading and needs fade-in when ready. */
|
||||
bool bWaitingForSublevelFadeIn = false;
|
||||
|
||||
/** True if we need to apply the fade-to-black once CameraManager is available. */
|
||||
bool bPendingFadeToBlack = false;
|
||||
|
||||
/** Disable custom post-process materials in the loaded sublevel to avoid stencil conflicts. */
|
||||
UFUNCTION()
|
||||
void DisableSublevelPostProcessMaterials();
|
||||
|
||||
/** Show/hide loading screen overlay on the HUD widget. */
|
||||
void ShowLoadingScreen(bool bShow);
|
||||
|
||||
void HandleEditorClick(FVector2D ScreenPos, bool bAdditive);
|
||||
};
|
||||
|
||||
@@ -45,6 +45,27 @@ void UPS_Editor_MainWidget::SetSceneSerializer(UPS_Editor_SceneSerializer* InSer
|
||||
TSharedRef<SWidget> UPS_Editor_MainWidget::RebuildWidget()
|
||||
{
|
||||
return SNew(SOverlay)
|
||||
// Loading screen (behind everything — first slot = bottom layer)
|
||||
+ SOverlay::Slot()
|
||||
.HAlign(HAlign_Fill)
|
||||
.VAlign(VAlign_Fill)
|
||||
[
|
||||
SAssignNew(LoadingOverlay, SBorder)
|
||||
.Visibility(EVisibility::Visible)
|
||||
.BorderImage(FCoreStyle::Get().GetBrush("WhiteBrush"))
|
||||
.BorderBackgroundColor(FLinearColor(0.0f, 0.0f, 0.0f, 1.0f))
|
||||
[
|
||||
SNew(SBox)
|
||||
.HAlign(HAlign_Center)
|
||||
.VAlign(VAlign_Center)
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString(TEXT("Loading...")))
|
||||
.Font(FCoreStyle::GetDefaultFontStyle("Bold", 18))
|
||||
.ColorAndOpacity(FLinearColor(0.7f, 0.7f, 0.7f, 1.0f))
|
||||
]
|
||||
]
|
||||
]
|
||||
// Top-left: Title + Toolbar
|
||||
+ SOverlay::Slot()
|
||||
.HAlign(HAlign_Left)
|
||||
@@ -999,6 +1020,19 @@ void UPS_Editor_MainWidget::NativeConstruct()
|
||||
}
|
||||
|
||||
PopulateSceneList();
|
||||
|
||||
// Hide loading overlay if no sublevel is being loaded
|
||||
if (APS_Editor_PlayerController* PC = Cast<APS_Editor_PlayerController>(GetOwningPlayer()))
|
||||
{
|
||||
if (PC->CurrentBaseLevel.IsEmpty())
|
||||
{
|
||||
ShowLoadingScreen(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowLoadingScreen(false);
|
||||
}
|
||||
}
|
||||
|
||||
void UPS_Editor_MainWidget::PopulateSceneList()
|
||||
@@ -2184,6 +2218,14 @@ void UPS_Editor_MainWidget::OnModeButtonClicked(EPS_Editor_TransformMode Mode)
|
||||
}
|
||||
}
|
||||
|
||||
void UPS_Editor_MainWidget::ShowLoadingScreen(bool bShow)
|
||||
{
|
||||
if (LoadingOverlay.IsValid())
|
||||
{
|
||||
LoadingOverlay->SetVisibility(bShow ? EVisibility::Visible : EVisibility::Collapsed);
|
||||
}
|
||||
}
|
||||
|
||||
void UPS_Editor_MainWidget::OnCloseClicked()
|
||||
{
|
||||
if (CloseConfirmOverlay.IsValid())
|
||||
|
||||
@@ -110,6 +110,13 @@ private:
|
||||
bool bSceneDirty = false;
|
||||
int32 LastKnownSpawnedCount = 0;
|
||||
|
||||
// Loading screen
|
||||
TSharedPtr<SWidget> LoadingOverlay;
|
||||
|
||||
public:
|
||||
/** Show/hide the loading overlay. */
|
||||
void ShowLoadingScreen(bool bShow);
|
||||
|
||||
// ---- Helpers ----
|
||||
TSharedRef<SWidget> BuildToolbar();
|
||||
TSharedRef<SWidget> BuildPropertiesPanel();
|
||||
|
||||
Reference in New Issue
Block a user