UE4 makes a simple analysis on the source of Slomo

This paper makes a simple analysis of the source of Slomo.

Slomo is used to pass the setting value to settimedivision:

void UCheatManager::Slomo(float NewTimeDilation)

Settimedivision is used to clamp the value once to ensure that the entered value is between the default maximum and minimum,

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

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:

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,

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

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.

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:

	// 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.

void AGameState::DefaultTimer()
	if (IsMatchInProgress())
		if (GetNetMode() != NM_DedicatedServer)

	GetWorldTimerManager().SetTimer(TimerHandle_DefaultTimer, this, &AGameState::DefaultTimer, GetWorldSettings()->GetEffectiveTimeDilation() / GetWorldSettings()->DemoPlayTimeDilation, true);

Tags: C++ Game Development UE4 unreal

Posted on Fri, 03 Dec 2021 12:03:22 -0500 by suzuki