(Feat) Fix Fade Loading
This commit is contained in:
@@ -3,9 +3,15 @@
|
||||
|
||||
#include "SurvivalGameInstance.h"
|
||||
|
||||
#include "ContentStreaming.h"
|
||||
#include "EnhancedInputSubsystems.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Engine/GameViewportClient.h"
|
||||
#include "Engine/LocalPlayer.h"
|
||||
#include "GameMapsSettings.h"
|
||||
#include "InputMappingContext.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "SessionSubsystem.h"
|
||||
#include "SurvivalUserSettings.h"
|
||||
#include "UserSettings/EnhancedInputUserSettings.h"
|
||||
|
||||
@@ -32,6 +38,175 @@ void USurvivalGameInstance::Init()
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("USurvivalGameInstance : GameUserSettingsClassName n'est pas configure dans DefaultEngine.ini, aucun reglage ne sera pris en compte."));
|
||||
}
|
||||
|
||||
// Filet de securite reseau. On s'abonne ici, une fois pour toutes : ces
|
||||
// evenements arrivent au pire moment, typiquement quand le monde est en
|
||||
// train d'etre detruit, et un abonnement porte par un acteur mourrait
|
||||
// justement avec lui.
|
||||
if (GEngine)
|
||||
{
|
||||
NetworkFailureHandle = GEngine->OnNetworkFailure().AddUObject(this, &USurvivalGameInstance::HandleNetworkFailure);
|
||||
TravelFailureHandle = GEngine->OnTravelFailure().AddUObject(this, &USurvivalGameInstance::HandleTravelFailure);
|
||||
}
|
||||
|
||||
PreLoadMapHandle = FCoreUObjectDelegates::PreLoadMap.AddUObject(this, &USurvivalGameInstance::HandlePreLoadMap);
|
||||
}
|
||||
|
||||
void USurvivalGameInstance::HandlePreLoadMap(const FString& MapName)
|
||||
{
|
||||
// On coupe AVANT que quoi que ce soit du nouveau monde n'existe. C'est le
|
||||
// premier controller pret qui rallumera, une fois son voile noir en place --
|
||||
// et il naitra forcement apres nous, ce qui garantit le bon ordre.
|
||||
SetWorldRenderingSuspended(true);
|
||||
UE_LOG(LogTemp, Log, TEXT("USurvivalGameInstance : chargement de [%s], rendu du monde suspendu."), *MapName);
|
||||
|
||||
// Le timer du GameInstance et non celui du monde : celui-ci est justement en
|
||||
// train d'etre detruit.
|
||||
GetTimerManager().SetTimer(
|
||||
RenderingResumeTimeout, this, &USurvivalGameInstance::ResumeWorldRendering,
|
||||
20.f, /*bLoop=*/false);
|
||||
}
|
||||
|
||||
void USurvivalGameInstance::BeginSceneWarmup(FSimpleDelegate OnComplete)
|
||||
{
|
||||
SceneWarmupCompleted = OnComplete;
|
||||
RemainingWarmupFrames = FMath::Max(SceneWarmupFrames, 0);
|
||||
WarmupStartTime = FPlatformTime::Seconds();
|
||||
|
||||
if (RemainingWarmupFrames <= 0)
|
||||
{
|
||||
SceneWarmupCompleted.ExecuteIfBound();
|
||||
SceneWarmupCompleted.Unbind();
|
||||
return;
|
||||
}
|
||||
|
||||
TickSceneWarmup();
|
||||
}
|
||||
|
||||
void USurvivalGameInstance::TickSceneWarmup()
|
||||
{
|
||||
const double Elapsed = FPlatformTime::Seconds() - WarmupStartTime;
|
||||
const bool bCapReached = Elapsed >= SceneWarmupMaxSeconds;
|
||||
|
||||
if (RemainingWarmupFrames > 0 && !bCapReached)
|
||||
{
|
||||
--RemainingWarmupFrames;
|
||||
|
||||
// SetTimerForNextTick et non un timer en secondes : une image rendue,
|
||||
// un tick. C'est l'unite dans laquelle Nanite et Lumen progressent.
|
||||
GetTimerManager().SetTimerForNextTick(this, &USurvivalGameInstance::TickSceneWarmup);
|
||||
return;
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("USurvivalGameInstance : chauffe terminee -- %d images rendues en %.0f ms%s."),
|
||||
SceneWarmupFrames - RemainingWarmupFrames, Elapsed * 1000.0,
|
||||
bCapReached ? TEXT(" (plafond atteint)") : TEXT(""));
|
||||
|
||||
// Copie avant execution : le rappel declenche un fondu, qui pourrait
|
||||
// theoriquement relancer une chauffe et ecraser le delegue en pleine main.
|
||||
FSimpleDelegate Completed = SceneWarmupCompleted;
|
||||
SceneWarmupCompleted.Unbind();
|
||||
Completed.ExecuteIfBound();
|
||||
}
|
||||
|
||||
void USurvivalGameInstance::SetWorldRenderingSuspended(bool bSuspended)
|
||||
{
|
||||
if (UGameViewportClient* Viewport = GetGameViewportClient())
|
||||
{
|
||||
Viewport->bDisableWorldRendering = bSuspended;
|
||||
}
|
||||
}
|
||||
|
||||
void USurvivalGameInstance::ResumeWorldRendering()
|
||||
{
|
||||
GetTimerManager().ClearTimer(RenderingResumeTimeout);
|
||||
|
||||
// Streaming vide AVANT de rallumer. UpdateResourceStreaming(0, true) traite
|
||||
// tout sans avoir besoin d'un point de vue -- ce qui tombe bien, le rendu
|
||||
// est encore coupe donc personne ne lui en a fourni -- puis on bloque le
|
||||
// temps que les requetes aboutissent.
|
||||
//
|
||||
// Ce blocage a lieu ECRAN NOIR : c'est du temps de chargement, pas une
|
||||
// saccade. C'est exactement ce que le moteur fait lui-meme a l'ouverture
|
||||
// d'une map, et c'est ce qui evite de voir le terrain se raffiner et les
|
||||
// modeles apparaitre un a un.
|
||||
const double StartTime = FPlatformTime::Seconds();
|
||||
const int32 PendingRequests = IStreamingManager::Get().StreamAllResources(FMath::Max(StreamingFlushTimeLimit, 0.f));
|
||||
const double ElapsedMs = (FPlatformTime::Seconds() - StartTime) * 1000.0;
|
||||
|
||||
SetWorldRenderingSuspended(false);
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("USurvivalGameInstance : rendu du monde repris apres %.0f ms de streaming (%d requete(s) restante(s))."),
|
||||
ElapsedMs, PendingRequests);
|
||||
}
|
||||
|
||||
void USurvivalGameInstance::Shutdown()
|
||||
{
|
||||
if (GEngine)
|
||||
{
|
||||
GEngine->OnNetworkFailure().Remove(NetworkFailureHandle);
|
||||
GEngine->OnTravelFailure().Remove(TravelFailureHandle);
|
||||
}
|
||||
|
||||
FCoreUObjectDelegates::PreLoadMap.Remove(PreLoadMapHandle);
|
||||
|
||||
Super::Shutdown();
|
||||
}
|
||||
|
||||
void USurvivalGameInstance::HandleNetworkFailure(UWorld* World, UNetDriver* NetDriver, ENetworkFailure::Type FailureType, const FString& ErrorString)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("USurvivalGameInstance : echec reseau [%s] -- %s"),
|
||||
ENetworkFailure::ToString(FailureType), *ErrorString);
|
||||
|
||||
// Depart deja en cours : c'est le cas NORMAL quand l'hote quitte, puisqu'il
|
||||
// a prevenu tout le monde avant de couper. On laisse les fondus finir.
|
||||
if (bReturningToMainMenu)
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("USurvivalGameInstance : depart deja en cours, echec ignore."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Ici en revanche personne n'a prevenu : hote qui plante, cable arrache,
|
||||
// relais Steam qui lache. C'est le filet de secours, sans fondu -- mieux
|
||||
// vaut un retour brutal au menu qu'un monde mort.
|
||||
ReturnToMainMenu();
|
||||
}
|
||||
|
||||
void USurvivalGameInstance::HandleTravelFailure(UWorld* World, ETravelFailure::Type FailureType, const FString& ErrorString)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("USurvivalGameInstance : echec de voyage [%s] -- %s"),
|
||||
ETravelFailure::ToString(FailureType), *ErrorString);
|
||||
|
||||
if (bReturningToMainMenu)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ReturnToMainMenu();
|
||||
}
|
||||
|
||||
void USurvivalGameInstance::ReturnToMainMenu()
|
||||
{
|
||||
bReturningToMainMenu = true;
|
||||
|
||||
// La session est morte avec la connexion : la detruire ici evite qu'elle
|
||||
// reste annoncee a tes amis, et surtout qu'elle bloque la creation de la
|
||||
// suivante.
|
||||
if (USessionSubsystem* Sessions = GetSubsystem<USessionSubsystem>())
|
||||
{
|
||||
Sessions->LeaveSession();
|
||||
}
|
||||
|
||||
// GetGameDefaultMap et pas un chemin en dur : c'est deja MenuScene dans
|
||||
// DefaultEngine.ini, et une seconde copie du chemin finirait par diverger.
|
||||
const FString MenuMap = UGameMapsSettings::GetGameDefaultMap();
|
||||
if (MenuMap.IsEmpty())
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("USurvivalGameInstance : aucune GameDefaultMap, impossible de revenir au menu."));
|
||||
return;
|
||||
}
|
||||
|
||||
UGameplayStatics::OpenLevel(this, FName(*MenuMap), /*bAbsolute=*/true);
|
||||
}
|
||||
|
||||
void USurvivalGameInstance::RegisterMappableContexts()
|
||||
|
||||
Reference in New Issue
Block a user