This paper makes a simple analysis of the source of Slomo.
Slomo is used to pass the setting value to settimedivision:
//UE_4.26\Engine\Source\Runtime\Engine\Private\CheatManager.cpp void UCheatManager::Slomo(float NewTimeDilation) { GetOuterAPlayerController()->GetWorldSettings()->SetTimeDilation(NewTimeDilation); }
Settimedivision is used to clamp the value once to ensure that the entered value is between the default maximum and minimum,
//UE_4.26\Engine\Source\Runtime\Engine\Private\WorldSettings.cpp float AWorldSettings::SetTimeDilation(float NewTimeDilation) { TimeDilation = FMath::Clamp(NewTimeDilation, MinGlobalTimeDilation, MaxGlobalTimeDilation); return TimeDilation; }
The default settings for the maximum and minimum values are in the UE_4.26\Engine\Config\BaseGame.ini
MinUndilatedFrameTime=0.0005 ; 2000 fps MaxUndilatedFrameTime=0.4 ; 2.5 fps MinGlobalTimeDilation=0.0001 MaxGlobalTimeDilation=20.0
Note that in addition to globaltimedivision, there is UndilatedFrameTime, which will be used later.
Settimedivision is called in setglobaltimedivision. To be exact, it is not called, but another judgment is made to determine whether the entered slomo conforms to minglobaltimedivision ~ maxglobaltimedivision:
//UE_4.26\Engine\Source\Runtime\Engine\Private\GameplayStatics.cpp void UGameplayStatics::SetGlobalTimeDilation(const UObject* WorldContextObject, float TimeDilation) { UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull); if (World != nullptr) { AWorldSettings* const WorldSettings = World->GetWorldSettings(); if (WorldSettings != nullptr) { float const ActualTimeDilation = WorldSettings->SetTimeDilation(TimeDilation); if (TimeDilation != ActualTimeDilation) { UE_LOG(LogBlueprintUserMessages, Warning, TEXT("Time Dilation must be between %f and %f. Clamped value to that range."), WorldSettings->MinGlobalTimeDilation, WorldSettings->MaxGlobalTimeDilation); } } } }
At this time, the timedivision returned by settimedivision has been changed in WorldSettings. It is formally called in geteffectivetimedivision. The return value is obtained by comprehensively considering the speed of Slomo,Matinee and DemoPlay, that is, the final effective expansion time,
//UE_4.26\Engine\Source\Runtime\Engine\Classes\GameFramework\WorldSettings.h virtual float GetEffectiveTimeDilation() const { return TimeDilation * MatineeTimeDilation * DemoPlayTimeDilation; }
I found it here by the way
The custom expansion time in actor is really a multiplication operation based on the final effective expansion time
//UE_4.26\Engine\Source\Runtime\Engine\Private\Actor.cpp float AActor::GetActorTimeDilation() const { // get actor custom time dilation // if you do slomo, that changes WorldSettings->TimeDilation // So multiply to get final TimeDilation return CustomTimeDilation * GetWorldSettings()->GetEffectiveTimeDilation(); } float AActor::GetActorTimeDilation(const UWorld& ActorWorld) const { checkSlow(&ActorWorld == GetWorld()); return CustomTimeDilation * ActorWorld.GetWorldSettings()->GetEffectiveTimeDilation(); }
Continue the main task,
Geteffectivetimedivision is used by FixupDeltaSeconds:
The function of FixupDeltaSeconds is somewhat similar to settimedivision. It also clamps the expansion speed within a certain range. However, the expansion speed here is jointly determined by Slomo, matinee animation speed and demoplay (demoplaytimedivision), and the clamping range also becomes UndilatedFrameTime. The default frame time is 0.005 seconds ~ 0.4 seconds.
//UE_4.26\Engine\Source\Runtime\Engine\Private\WorldSettings.cpp float AWorldSettings::FixupDeltaSeconds(float DeltaSeconds, float RealDeltaSeconds) { // DeltaSeconds is assumed to be fully dilated at this time, so we will dilate the clamp range as well //TimeDilation * MatineeTimeDilation * DemoPlayTimeDilation float const Dilation = GetEffectiveTimeDilation(); float const MinFrameTime = MinUndilatedFrameTime * Dilation; float const MaxFrameTime = MaxUndilatedFrameTime * Dilation; // clamp frame time according to desired limits return FMath::Clamp(DeltaSeconds, MinFrameTime, MaxFrameTime); }
In all solutions, only four places have the function FixupDeltaSeconds, namely, the call of each frame of the level, the replay system call, the time axis call, and the function definition in the world setting. Therefore, we mainly look at the call of each frame of the level:
//UE_4.26\Engine\Source\Runtime\Engine\Private\LevelTick.cpp // Save off actual delta float RealDeltaSeconds = DeltaSeconds; // apply time multipliers //GetEffectiveTimeDilation()==TimeDilation * MatineeTimeDilation * DemoPlayTimeDilation DeltaSeconds *= Info->GetEffectiveTimeDilation(); // Handle clamping of time to an acceptable value const float GameDeltaSeconds = Info->FixupDeltaSeconds(DeltaSeconds, RealDeltaSeconds); check(GameDeltaSeconds >= 0.0f); DeltaSeconds = GameDeltaSeconds; DeltaTimeSeconds = DeltaSeconds; UnpausedTimeSeconds += DeltaSeconds;
Let's look at this part of Tick. First save the current expansion seconds as RealDeltaSeconds, then read the Slomo speed timedivision transition animation speed DemoPlay running speed defined by us, set it as the current expansion seconds, then define a GameDeltaSeconds to save the clamped speed, and then assign it to the current expansion seconds DeltaSeconds and RealDeltaSeconds, Finish Slomo once here.
In addition, there is a default timer in GameState, which also uses geteffectivetimedivision. There is no FixupDeltaSeconds. It is speculated that the default timer is also affected by Slomo and not by the default frame time.
//UE_4.26\Engine\Source\Runtime\Engine\Private\GameState.cpp void AGameState::DefaultTimer() { if (IsMatchInProgress()) { ++ElapsedTime; if (GetNetMode() != NM_DedicatedServer) { OnRep_ElapsedTime(); } } GetWorldTimerManager().SetTimer(TimerHandle_DefaultTimer, this, &AGameState::DefaultTimer, GetWorldSettings()->GetEffectiveTimeDilation() / GetWorldSettings()->DemoPlayTimeDilation, true); }