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/LevelStreamingDynamic.h"
|
||||||
#include "Engine/PostProcessVolume.h"
|
#include "Engine/PostProcessVolume.h"
|
||||||
#include "PS_Editor_CameraStart.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 "PS_Editor_GameInstanceSubsystem.h"
|
||||||
#include "GameFramework/GameModeBase.h"
|
#include "GameFramework/GameModeBase.h"
|
||||||
#include "GameFramework/GameStateBase.h"
|
#include "GameFramework/GameStateBase.h"
|
||||||
@@ -136,6 +139,14 @@ void APS_Editor_PlayerController::Tick(float DeltaTime)
|
|||||||
{
|
{
|
||||||
Super::Tick(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)
|
if (SpawnManager)
|
||||||
{
|
{
|
||||||
SpawnManager->TickEditorMode(DeltaTime);
|
SpawnManager->TickEditorMode(DeltaTime);
|
||||||
@@ -253,6 +264,17 @@ void APS_Editor_PlayerController::CancelPointPlacement()
|
|||||||
FinishPointPlacement();
|
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)
|
void APS_Editor_PlayerController::LoadBaseLevelAsSublevel(const FString& LevelName, const FString& MapPath)
|
||||||
{
|
{
|
||||||
// Unload previous sublevel first
|
// Unload previous sublevel first
|
||||||
@@ -294,6 +316,19 @@ void APS_Editor_PlayerController::LoadBaseLevelAsSublevel(const FString& LevelNa
|
|||||||
return;
|
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)
|
// Use MapPath if provided, otherwise fall back to LevelName (legacy behavior)
|
||||||
const FString& LevelToLoad = MapPath.IsEmpty() ? LevelName : MapPath;
|
const FString& LevelToLoad = MapPath.IsEmpty() ? LevelName : MapPath;
|
||||||
|
|
||||||
@@ -362,6 +397,21 @@ void APS_Editor_PlayerController::DisableSublevelPostProcessMaterials()
|
|||||||
*CameraStart->GetActorLocation().ToString());
|
*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)
|
void APS_Editor_PlayerController::HandleEditorClick(FVector2D ScreenPos, bool bAdditive)
|
||||||
|
|||||||
@@ -136,9 +136,18 @@ private:
|
|||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
TObjectPtr<ULevelStreamingDynamic> CurrentSublevel;
|
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. */
|
/** Disable custom post-process materials in the loaded sublevel to avoid stencil conflicts. */
|
||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
void DisableSublevelPostProcessMaterials();
|
void DisableSublevelPostProcessMaterials();
|
||||||
|
|
||||||
|
/** Show/hide loading screen overlay on the HUD widget. */
|
||||||
|
void ShowLoadingScreen(bool bShow);
|
||||||
|
|
||||||
void HandleEditorClick(FVector2D ScreenPos, bool bAdditive);
|
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()
|
TSharedRef<SWidget> UPS_Editor_MainWidget::RebuildWidget()
|
||||||
{
|
{
|
||||||
return SNew(SOverlay)
|
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
|
// Top-left: Title + Toolbar
|
||||||
+ SOverlay::Slot()
|
+ SOverlay::Slot()
|
||||||
.HAlign(HAlign_Left)
|
.HAlign(HAlign_Left)
|
||||||
@@ -999,6 +1020,19 @@ void UPS_Editor_MainWidget::NativeConstruct()
|
|||||||
}
|
}
|
||||||
|
|
||||||
PopulateSceneList();
|
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()
|
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()
|
void UPS_Editor_MainWidget::OnCloseClicked()
|
||||||
{
|
{
|
||||||
if (CloseConfirmOverlay.IsValid())
|
if (CloseConfirmOverlay.IsValid())
|
||||||
|
|||||||
@@ -110,6 +110,13 @@ private:
|
|||||||
bool bSceneDirty = false;
|
bool bSceneDirty = false;
|
||||||
int32 LastKnownSpawnedCount = 0;
|
int32 LastKnownSpawnedCount = 0;
|
||||||
|
|
||||||
|
// Loading screen
|
||||||
|
TSharedPtr<SWidget> LoadingOverlay;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Show/hide the loading overlay. */
|
||||||
|
void ShowLoadingScreen(bool bShow);
|
||||||
|
|
||||||
// ---- Helpers ----
|
// ---- Helpers ----
|
||||||
TSharedRef<SWidget> BuildToolbar();
|
TSharedRef<SWidget> BuildToolbar();
|
||||||
TSharedRef<SWidget> BuildPropertiesPanel();
|
TSharedRef<SWidget> BuildPropertiesPanel();
|
||||||
|
|||||||
Reference in New Issue
Block a user