bSubstepping

bSubstepping

#Overview

name: bSubstepping

The value of this variable can be defined or overridden in .ini config files. 1 .ini config file referencing this setting variable.

It is referenced in 11 C++ source files.

#Summary

#Usage in the C++ source code

The purpose of bSubstepping is to control whether the physics simulation should be subdivided into smaller time steps (substeps) during each frame. This setting is part of the physics engine in Unreal Engine 5 and is used to improve the accuracy and stability of physics simulations, especially in scenarios with fast-moving objects or complex interactions.

The bSubstepping variable is primarily used in the physics engine subsystem of Unreal Engine 5. It is referenced in the PhysicsSettings, BodyInstance, and ChaosScene modules, which are core components of the physics simulation system.

The value of this variable is set in the UPhysicsSettings class, which is likely configurable through the project settings in the Unreal Engine editor.

Several other variables interact with bSubstepping, including:

Developers must be aware of the following when using this variable:

  1. It is marked as experimental, and certain functionality might not work correctly when enabled.
  2. Enabling substepping can affect performance, as it may increase the number of physics calculations per frame.
  3. When substepping is enabled, it alters how the physics delta time is calculated and applied.

Best practices when using this variable include:

  1. Test thoroughly when enabling substepping, as it may introduce unexpected behavior in physics simulations.
  2. Adjust related variables (MaxSubstepDeltaTime, MaxSubsteps) to find the right balance between simulation accuracy and performance.
  3. Monitor performance impacts when enabling substepping, especially in complex scenes or on target platforms with limited resources.
  4. Consider using bSubstepping only when dealing with fast-moving objects or complex physics interactions that require higher accuracy.
  5. Be aware that enabling substepping may affect other physics-related settings and behaviors, so ensure to test all physics-dependent systems in your game when modifying this setting.

#Setting Variables

#References In INI files

Location: <Workspace>/Projects/Lyra/Config/DefaultEngine.ini:338, section: [/Script/Engine.PhysicsSettings]

#References in C++ code

#Callsites

This variable is referenced in the following C++ source code:

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Classes/PhysicsEngine/PhysicsSettings.h:205

Scope (from outer to inner):

file
class        class UPhysicsSettings : public UPhysicsSettingsCore

Source code excerpt:

	/** Whether to substep the physics simulation. This feature is still experimental. Certain functionality might not work correctly*/
	UPROPERTY(config, EditAnywhere, Category = Framerate)
	bool bSubstepping;

	/** Whether to substep the async physics simulation. This feature is still experimental. Certain functionality might not work correctly*/
	UPROPERTY(config, EditAnywhere, Category = Framerate)
	bool bSubsteppingAsync;

	/** Whether to tick physics simulation on an async thread. This feature is still experimental. Certain functionality might not work correctly*/

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/PhysicsEngine/BodyInstance.cpp:4468

Scope (from outer to inner):

file
function     void FBodyInstance::UpdateInterpolateWhenSubStepping

Source code excerpt:

void FBodyInstance::UpdateInterpolateWhenSubStepping()
{
	if(UPhysicsSettings::Get()->bSubstepping)
	{
		// We interpolate based around our current collision enabled flag
		ECollisionEnabled::Type UseCollisionEnabled = ECollisionEnabled::NoCollision;
		if(OwnerComponent.IsValid() && OwnerComponent.Get()->GetBodyInstance() != this)
		{
			UseCollisionEnabled = OwnerComponent->GetCollisionEnabled();

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/PhysicsEngine/PhysLevel.cpp:147

Scope (from outer to inner):

file
function     void UWorld::SetupPhysicsTickFunctions

Source code excerpt:

	static const auto CVar_MaxPhysicsDeltaTime = IConsoleManager::Get().FindTConsoleVariableDataFloat(TEXT("p.MaxPhysicsDeltaTime"));
	PhysScene->SetUpForFrame(&DefaultGravity, DeltaSeconds, UPhysicsSettings::Get()->MinPhysicsDeltaTime, UPhysicsSettings::Get()->MaxPhysicsDeltaTime,
		UPhysicsSettings::Get()->MaxSubstepDeltaTime, UPhysicsSettings::Get()->MaxSubsteps, UPhysicsSettings::Get()->bSubstepping);
}

void UWorld::StartPhysicsSim()
{
	FPhysScene* PhysScene = GetPhysicsScene();
	if (PhysScene == NULL)

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/PhysicsEngine/PhysicsSettings.cpp:20

Scope (from outer to inner):

file
function     UPhysicsSettings::UPhysicsSettings

Source code excerpt:

	, MinPhysicsDeltaTime(UE_SMALL_NUMBER)
	, MaxPhysicsDeltaTime(1.f / 30.f)
	, bSubstepping(false)
	, bTickPhysicsAsync(false)
	, AsyncFixedTimeStepSize(1.f / 30.f)
	, MaxSubstepDeltaTime(1.f / 60.f)
	, MaxSubsteps(6)
	, SyncSceneSmoothingFactor(0.0f)
	, InitialAverageFrameRate(1.f / 60.f)

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/PhysicsEngine/PhysicsSettings.cpp:89

Scope (from outer to inner):

file
function     bool UPhysicsSettings::CanEditChange

Source code excerpt:

		if(Name == TEXT("MaxPhysicsDeltaTime") || Name == TEXT("SyncSceneSmoothingFactor") || Name == TEXT("AsyncSceneSmoothingFactor") || Name == TEXT("InitialAverageFrameRate"))
		{
			bIsEditable = !bSubstepping;
		}
	}

	return bIsEditable;
}

#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/Framework/PhysicsSolverBase.cpp:425

Scope (from outer to inner):

file
namespace    Chaos
function     FGraphEventRef FPhysicsSolverBase::AdvanceAndDispatch_External

Source code excerpt:

	FGraphEventRef FPhysicsSolverBase::AdvanceAndDispatch_External(FReal InDt)
	{
		const bool bSubstepping = MMaxSubSteps > 1;
		SetSolverSubstep_External(bSubstepping);
		const FReal DtWithPause = bPaused_External ? 0.0f : InDt;
		FReal InternalDt = DtWithPause;
		int32 NumSteps = 1;

		if(IsUsingFixedDt())
		{

#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/Framework/PhysicsSolverBase.cpp:449

Scope (from outer to inner):

file
namespace    Chaos
function     FGraphEventRef FPhysicsSolverBase::AdvanceAndDispatch_External

Source code excerpt:

			}
		}
		else if (bSubstepping && InDt > 0)
		{
			NumSteps = FMath::CeilToInt32(DtWithPause / MMaxDeltaTime);
			if (NumSteps > MMaxSubSteps)
			{
				// Hitting this case means we're losing time, given the constraints of MaxSteps and MaxDt we can't
				// fully handle the Dt requested, the simulation will appear to the viewer to run slower than realtime

#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/Framework/PhysicsSolverBase.cpp:551

Scope (from outer to inner):

file
namespace    Chaos
function     FGraphEventRef FPhysicsSolverBase::AdvanceAndDispatch_External

Source code excerpt:

			// This break is mainly here to satisfy unit testing. The call to StepInternalTime_External will decrement the
			// delay in the marshaling manager and throw of tests that are explicitly testing for propagation delays
			if (IsUsingAsyncResults() == false && !bSubstepping)
			{
				break;
			}
		}
		if (AsyncBlockMode == EAsyncBlockMode::DoNoBlock)
		{

#Loc: <Workspace>/Engine/Source/Runtime/PhysicsCore/Private/ChaosScene.cpp:332

Scope (from outer to inner):

file
function     void FChaosScene::SetUpForFrame

Source code excerpt:

}

void FChaosScene::SetUpForFrame(const FVector* NewGrav,float InDeltaSeconds /*= 0.0f*/,float InMinPhysicsDeltaTime /*= 0.0f*/,float InMaxPhysicsDeltaTime /*= 0.0f*/,float InMaxSubstepDeltaTime /*= 0.0f*/,int32 InMaxSubsteps,bool bSubstepping)
{
	using namespace Chaos;
	SetGravity(*NewGrav);

	InDeltaSeconds *= MNetworkDeltaTimeScale;

	if(bSubstepping)
	{
		MDeltaTime = FMath::Min(InDeltaSeconds, InMaxSubsteps * InMaxSubstepDeltaTime);
	}
	else
	{
		MDeltaTime = InMaxPhysicsDeltaTime > 0.f ? FMath::Min(InDeltaSeconds, InMaxPhysicsDeltaTime) : InDeltaSeconds;

#Loc: <Workspace>/Engine/Source/Runtime/PhysicsCore/Private/ChaosScene.cpp:350

Scope (from outer to inner):

file
function     void FChaosScene::SetUpForFrame

Source code excerpt:

	if(FPhysicsSolver* Solver = GetSolver())
	{
		if(bSubstepping)
		{
			Solver->SetMaxDeltaTime_External(InMaxSubstepDeltaTime);
			Solver->SetMaxSubSteps_External(InMaxSubsteps);
		} 
		else
		{

#Loc: <Workspace>/Engine/Source/Runtime/PhysicsCore/Public/Chaos/ChaosScene.h:138

Scope: file

Source code excerpt:


	PHYSICSCORE_API void StartFrame();
	PHYSICSCORE_API void SetUpForFrame(const FVector* NewGrav,float InDeltaSeconds,float InMinPhysicsDeltaTime,float InMaxPhysicsDeltaTime,float InMaxSubstepDeltaTime,int32 InMaxSubsteps,bool bSubstepping);
	PHYSICSCORE_API void EndFrame();

	DECLARE_MULTICAST_DELEGATE_OneParam(FOnPhysScenePostTick,FChaosScene*);
	FOnPhysScenePostTick OnPhysScenePostTick;

	PHYSICSCORE_API void KillSafeAsyncTasks();