328 lines
8.8 KiB
C++
328 lines
8.8 KiB
C++
// Fill out your copyright notice in the Description page of Project Settings.
|
|
|
|
|
|
#include "LoadingManager.h"
|
|
|
|
#include "AssetCompilingManager.h"
|
|
#include "ShaderCompiler.h"
|
|
#include "Widgets/SCompoundWidget.h"
|
|
#include "Blueprint/UserWidget.h"
|
|
#include "MoviePlayer.h"
|
|
#include "ProjectT/ProjectT.h"
|
|
#include "ProjectT/Data/Gen/GenerateEnumStage.h"
|
|
#include "ProjectT/System/Core/Common/GlobalUtilsLibrary.h"
|
|
#include "ProjectT/System/Core/Interfaces/LoadableObject.h"
|
|
#include "ProjectT/System/Core/Settings/GeneralSettings/CoreLoadingScreenSettings.h"
|
|
#include "Widgets/Images/SThrobber.h"
|
|
|
|
enum class ELoadingScenario : uint8
|
|
{
|
|
Ignore = 0,
|
|
Process
|
|
};
|
|
|
|
TMap<EStage, ELoadingScenario> LoadingScenarioMap =
|
|
{
|
|
{EStage::None, ELoadingScenario::Ignore},
|
|
{EStage::L_Entry, ELoadingScenario::Process},
|
|
{EStage::L_Lobby, ELoadingScenario::Ignore},
|
|
{EStage::L_Intro, ELoadingScenario::Ignore},
|
|
};
|
|
|
|
class SLoadingScreenSimple : public SCompoundWidget
|
|
{
|
|
public:
|
|
SLATE_BEGIN_ARGS(SLoadingScreenSimple) {}
|
|
SLATE_END_ARGS()
|
|
|
|
void Construct(const FArguments& InArgs)
|
|
{
|
|
ChildSlot
|
|
[
|
|
SNew(SVerticalBox)
|
|
+SVerticalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.HAlign(HAlign_Center)
|
|
[
|
|
SNew(SThrobber)
|
|
.Visibility(EVisibility::SelfHitTestInvisible)
|
|
]
|
|
+SVerticalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.HAlign(HAlign_Center)
|
|
[
|
|
SNew(STextBlock)
|
|
.Visibility(EVisibility::SelfHitTestInvisible)
|
|
.Text(FText::FromString(TEXT("Loading...")))
|
|
]
|
|
];
|
|
}
|
|
};
|
|
|
|
|
|
ULoadingManager::ULoadingManager():
|
|
HoldLoadingScreenTime(0.f),
|
|
MaxHoldLoadingScreenTime(0.f),
|
|
LoadingWidgetClass(nullptr),
|
|
LoadingScreenWidget(nullptr),
|
|
bPreLoadingFlag(0),
|
|
bStartupLoading(0),
|
|
LoadingState(ELoadingState::None)
|
|
{
|
|
}
|
|
|
|
void ULoadingManager::Deinitialize()
|
|
{
|
|
Super::Deinitialize();
|
|
|
|
FCoreUObjectDelegates::PreLoadMap.Remove(PreLoadMapHandle);
|
|
FCoreUObjectDelegates::PostLoadMapWithWorld.Remove(PostLoadMapHandle);
|
|
|
|
RemoveLoadingScreen();
|
|
LoadingScreenWidget = nullptr;
|
|
LoadingWidgetClass = nullptr;
|
|
|
|
OnLoadingStateDynamic.Clear();
|
|
OnLoadingStateStatic = nullptr;
|
|
|
|
if(TickerHandle.IsValid())
|
|
{
|
|
FTSTicker::GetCoreTicker().RemoveTicker(TickerHandle);
|
|
}
|
|
|
|
PreLoadObjects.Empty();
|
|
}
|
|
|
|
void ULoadingManager::Initialize(FSubsystemCollectionBase& Collection)
|
|
{
|
|
Super::Initialize(Collection);
|
|
|
|
PreLoadMapHandle = FCoreUObjectDelegates::PreLoadMapWithContext.AddUObject(this, &ULoadingManager::StartLoadMap);
|
|
PostLoadMapHandle = FCoreUObjectDelegates::PostLoadMapWithWorld.AddUObject(this, &ULoadingManager::EndLoadMap);
|
|
}
|
|
|
|
bool ULoadingManager::ShouldCreateSubsystem(UObject* Outer) const
|
|
{
|
|
return Super::ShouldCreateSubsystem(Outer);
|
|
}
|
|
|
|
void ULoadingManager::RegisterPreLoadableObject(const TScriptInterface<ILoadableObject>& LoadableObject)
|
|
{
|
|
PreLoadObjects.Add(LoadableObject.GetObject());
|
|
}
|
|
|
|
void ULoadingManager::UnRegisterPreLoadableObject(const TScriptInterface<ILoadableObject>& LoadableObject)
|
|
{
|
|
PreLoadObjects.RemoveSwap(LoadableObject.GetObject());
|
|
}
|
|
|
|
void ULoadingManager::ReceiveLoadingState(const FOnLoadingStateStatic& Callback)
|
|
{
|
|
if(!Callback.IsBound())
|
|
NMT_MSG_CHECKF(0, "Callback Delegate Is Not Bound");
|
|
|
|
if(!bStartupLoading)
|
|
{
|
|
Callback.Execute(ELoadingState::End);
|
|
return;
|
|
}
|
|
|
|
if(!OnLoadingStateStatic.IsBound())
|
|
OnLoadingStateStatic = Callback;
|
|
}
|
|
|
|
void ULoadingManager::StartLoadMap(const FWorldContext& WorldContext, const FString& MapName)
|
|
{
|
|
if(WorldContext.OwningGameInstance == GetGameInstance())
|
|
{
|
|
if(GEngine->IsInitialized())
|
|
{
|
|
const FString BaseFilename = FPaths::GetBaseFilename(MapName);
|
|
const EStage CurrentStage = UGlobalUtilsLibrary::GetStringToEnum<EStage>(BaseFilename);
|
|
BeginLoadingScreen(CurrentStage);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ULoadingManager::PreLoadingScreen(const EStage Stage)
|
|
{
|
|
bPreLoadingFlag = true;
|
|
BeginLoadingScreen(Stage);
|
|
}
|
|
|
|
void ULoadingManager::BeginLoadingScreen(const EStage Stage)
|
|
{
|
|
ELoadingScenario* Found = LoadingScenarioMap.Find(Stage);
|
|
if(Found)
|
|
{
|
|
if(*Found == ELoadingScenario::Ignore) return;
|
|
}
|
|
|
|
const UCoreLoadingScreenSettings* Settings = GetDefault<UCoreLoadingScreenSettings>();
|
|
if(Stage == EStage::L_Entry)
|
|
{
|
|
if(Settings->bEnableStartUpMovie && !Settings->StartUpMoviePath.IsEmpty())
|
|
{
|
|
bStartupLoading = true;
|
|
FLoadingScreenAttributes LoadingScreen;
|
|
LoadingScreen.bAutoCompleteWhenLoadingCompletes = false;
|
|
LoadingScreen.bMoviesAreSkippable = true;
|
|
LoadingScreen.MoviePaths.Add(Settings->StartUpMoviePath);
|
|
GetMoviePlayer()->SetupLoadingScreen(LoadingScreen);
|
|
GetMoviePlayer()->OnMoviePlaybackFinished().AddWeakLambda(this, [this]()
|
|
{
|
|
RemoveLoadingScreen();
|
|
PropagateLoadingState(ELoadingState::End);
|
|
LoadingState = ELoadingState::None;
|
|
bPreLoadingFlag = false;
|
|
});
|
|
return;
|
|
}
|
|
if(!Settings->bEnableStartUpMovie && Settings->PreLoadingScreenClassPath.IsValid())
|
|
{
|
|
LoadingWidgetPath = Settings->PreLoadingScreenClassPath;
|
|
LoadingWidgetClass = LoadingWidgetPath.TryLoadClass<UUserWidget>();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LoadingWidgetPath = Settings->PostLoadingScreenClassPath;
|
|
LoadingWidgetClass = LoadingWidgetPath.TryLoadClass<UUserWidget>();
|
|
}
|
|
|
|
if(LoadingState == ELoadingState::None)
|
|
{
|
|
UGameInstance* GI = GetGameInstance();
|
|
NMT_CHECKF(GI);
|
|
|
|
if(UGameViewportClient* GameViewportClient = GI->GetGameViewportClient())
|
|
GameViewportClient->RemoveAllViewportWidgets();
|
|
|
|
MaxHoldLoadingScreenTime = Settings->MinimumLoadingScreenTime;
|
|
ShowLoadingScreen();
|
|
}
|
|
}
|
|
|
|
void ULoadingManager::ShowLoadingScreen()
|
|
{
|
|
const UCoreLoadingScreenSettings* Settings = GetDefault<UCoreLoadingScreenSettings>();
|
|
if(Settings->bForceLoadingComplete)
|
|
{
|
|
EndLoadingScreen();
|
|
return;
|
|
}
|
|
|
|
UGameInstance* GI = GetGameInstance();
|
|
NMT_CHECKF(GI);
|
|
|
|
if(!LoadingWidgetClass)
|
|
{
|
|
LoadingScreenWidget = SNew(SLoadingScreenSimple);
|
|
}
|
|
else
|
|
{
|
|
if(UUserWidget* UserWidget = UUserWidget::CreateWidgetInstance(*GI, LoadingWidgetClass, NAME_None))
|
|
{
|
|
LoadingScreenWidget = UserWidget->TakeWidget();
|
|
}
|
|
else
|
|
{
|
|
LoadingScreenWidget = SNew(SLoadingScreenSimple);
|
|
}
|
|
}
|
|
|
|
NMT_CHECKF(LoadingScreenWidget);
|
|
if(UGameViewportClient* GameViewportClient = GI->GetGameViewportClient())
|
|
{
|
|
GameViewportClient->AddViewportWidgetContent(LoadingScreenWidget.ToSharedRef(), Settings->ZOrder);
|
|
}
|
|
|
|
PropagateLoadingState(ELoadingState::Start);
|
|
TickerHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateUObject(this, &ULoadingManager::UpdateLoadingScreen));
|
|
}
|
|
|
|
bool ULoadingManager::UpdateLoadingScreen(float DeltaTime)
|
|
{
|
|
HoldLoadingScreenTime += DeltaTime;
|
|
|
|
const bool bHalfTimeDone = (HoldLoadingScreenTime < MaxHoldLoadingScreenTime * 0.5f);
|
|
if(bHalfTimeDone) return true;
|
|
|
|
const bool bWithinMaxHold = (HoldLoadingScreenTime < MaxHoldLoadingScreenTime);
|
|
|
|
/**
|
|
* NOTE: GShaderCompilingManager 글로벌 변수는 PIE(PlayInEditor), StandAlone 일때 유효함
|
|
* 또한 빌드시 이미 World->Level 은 셰이더 컴파일을 기본적으로 프리 캐싱해서 배포하기 때문에 유도할 이유가 없음
|
|
* 만약 메모리에 아직 올리지않은 에셋단위거나 직접적으로 로드를 해야한다면 FStreamableManager를 통해 비동기로드 설계를 유도해야할 것 같음
|
|
*/
|
|
#if WITH_EDITOR
|
|
const bool bCompiling = GShaderCompilingManager->IsCompiling();
|
|
#else
|
|
const bool bCompiling = false;
|
|
#endif
|
|
|
|
if(!bCompiling && !bPreLoadingFlag)
|
|
{
|
|
if(!bWithinMaxHold && PreLoadObjects.IsEmpty())
|
|
{
|
|
EndLoadingScreen();
|
|
return false;
|
|
}
|
|
if(HoldLoadingScreenTime > 20.f && !PreLoadObjects.IsEmpty())
|
|
{
|
|
auto FailedObjects = PreLoadObjects.FilterByPredicate([](const TWeakInterfacePtr<ILoadableObject>& Object)
|
|
{
|
|
return Object->HasFinishedLoading();
|
|
});
|
|
for(TWeakInterfacePtr<ILoadableObject> Obj : FailedObjects)
|
|
{
|
|
NMT_LOGF("Loading process timed out. Inspect the following object(s) still loading: %s", *Obj.GetWeakObjectPtr()->GetName());
|
|
}
|
|
NMT_CHECKF(0);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
PropagateLoadingState(ELoadingState::Update);
|
|
return true;
|
|
}
|
|
|
|
void ULoadingManager::RemoveLoadingScreen()
|
|
{
|
|
if(!LoadingScreenWidget.IsValid()) return;
|
|
if(TickerHandle.IsValid()) FTSTicker::GetCoreTicker().RemoveTicker(TickerHandle);
|
|
|
|
if(UGameViewportClient* GameViewportClient = GetGameInstance()->GetGameViewportClient())
|
|
{
|
|
GameViewportClient->RemoveViewportWidgetContent(LoadingScreenWidget.ToSharedRef());
|
|
LoadingScreenWidget.Reset();
|
|
}
|
|
|
|
HoldLoadingScreenTime = 0.f;
|
|
MaxHoldLoadingScreenTime = 0.f;
|
|
}
|
|
|
|
void ULoadingManager::EndLoadingScreen()
|
|
{
|
|
RemoveLoadingScreen();
|
|
PropagateLoadingState(ELoadingState::End);
|
|
LoadingState = ELoadingState::None;
|
|
}
|
|
|
|
void ULoadingManager::EndLoadMap(UWorld* World)
|
|
{
|
|
if(bPreLoadingFlag)
|
|
bPreLoadingFlag = false;
|
|
}
|
|
|
|
void ULoadingManager::PropagateLoadingState(const ELoadingState NewState)
|
|
{
|
|
LoadingState = NewState;
|
|
|
|
if(OnLoadingStateDynamic.IsBound())
|
|
OnLoadingStateDynamic.Broadcast(LoadingState);
|
|
|
|
if(OnLoadingStateStatic.IsBound())
|
|
OnLoadingStateStatic.Execute(LoadingState);
|
|
} |