Physics substepping






To apply a standard force to any rigidbody in unreal we do the following in a postphysics tick, being f the desired acceleration(gravity in this case):

f=m*a

FVector f = FVector(0.0f, 0.0f, -980.0f) *GetMass();
//Multiplied by delta time by default!
AddForce(f);

By doing this we get a nice and reliable way to interact with the physics simulation.


Substepping?


I will now explain very briefly how unreal physics work, for a detailed and awesome explanation please see the links at the end of the post.

By default unreal uses the default tick time to simulate the physics, this causes the physics simulation speed to fluctuate as the framerate does. Unreal have a couple ways to solve this, one is MaxPhysicsDeltaTime.This sets a maximum delta time to the simulation so the physics keep a specified constant delta time regardless higher frame rate fluctuations.

The other solution unreal have is substepping, this runs the physics simulation as many times as needed in between frames if the framerate is not high enough to achieve the desired physics refresh rate.




In case we use substepping and we need finetune the forces every physic iteration instead every frame, we will need to add a custom substep tick. This is the main goal of this post, as in many projects (such as racing games or simulators) the substepping is in most cases mandatory, and being able to control the physics simulation is not an easy task.

So lets se how to setup the substepping tick, in this case we are gonna add It to a component.


Adding the necesary modules


First we need to add a couple of modules in PublicDependencyModuleNames, this are the PhysX and the APEX ones. To do this you have to add them to yourProjectName.Build.cs, located in Source->yourProjectName.
Keep in mind that after this you may have to close unreal and visual studio. Then delete the .vs, Binaries, Intermediate folders, the yourProjectName.sln file and right click on yourProjectName.uproject and Generate Visual Studio project files to totally integrate the new modules in the project.

using System;
 
using UnrealBuildTool;

public class Experimental17 : ModuleRules
{
 public Experimental17(ReadOnlyTargetRules Target) : base(Target)
 {
  PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

  PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "PhysX", "APEX" });
 }
}


We are gonna use mainly a couple of classes, FBodyInstance and PxRigidbody.

FBodyInstance: contains the physical representation of an object.
PxRigidbody: this is the physX class of a rigid body


So we are gonna do a component and etc etc because the substep doesnt work with tick etc etc simulation slowdown etc etc

Creating the component and adding the substepTick


Lets start by creating a new component from staticMeshComponent. Unreal have a specific delegate to bind a function to the substep tick, so lets create the delegate. In the tick function we have to add our delegate to the FBodyInstance sub stepping (Be sure to do this only when simulating physics is enabled!).


 
 //Substep tick function
 void SubstepTick(float DeltaTime, FBodyInstance* BodyInstance);


private:
 //custom physics delegate
 FCalculateCustomPhysics OnCalculateCustomPhysics;
 




 

UPhysMeshComponent::UPhysMeshComponent()
{
 PrimaryComponentTick.bCanEverTick = true;
 PrimaryComponentTick.bStartWithTickEnabled = true;

 //Binding SubstepTick function with delegate
 OnCalculateCustomPhysics.BindUObject(this, &UPhysMeshComponent::SubstepTick);
}


void UPhysMeshComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{

 Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

 if (IsSimulatingPhysics()) {
 //Adding the delegate to the substep
 GetBodyInstance()->AddCustomPhysics(OnCalculateCustomPhysics);
 }
}




The unreal rigidbody update with the default tick, so we are going to create some functions to acess the rigidbody attributes in the substep tick. Remember to include PhysIncludes.h to work with BodyInstances and PhysX clases.


#pragma once

#include "CoreMinimal.h"
#include "Components/MeshComponent.h"
#include "PhysMeshComponent.generated.h"

/**
 * 
 */
UCLASS()
class EXPERIMENTAL17_API UPhysMeshComponent : public UMeshComponent
{
 GENERATED_BODY()
 
public:
 UPhysMeshComponent();

 virtual void BeginPlay() override;
 virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

 //Substep tick function
 void SubstepTick(float DeltaTime, FBodyInstance* BodyInstance);


private:
 //custom physics delegate
 FCalculateCustomPhysics OnCalculateCustomPhysics;
 

 //rigidbody easy access(no sub tick update on usual rigidbody)
 const FPhysicsActorHandle* PActorHandle;


 //get artibutes directly from the PrigidBody
 FORCEINLINE FVector  GetLocation();
 FORCEINLINE FRotator  GetRotation();
 FORCEINLINE FVector  GetLinearVelocity();
 FORCEINLINE FVector  GetAngularVelocity();

};





#include "PhysMeshComponent.h"

#include 


UPhysMeshComponent::UPhysMeshComponent()
{
 PrimaryComponentTick.bCanEverTick = true;
 PrimaryComponentTick.bStartWithTickEnabled = true;

 //Binding SubstepTick function with delegate
 OnCalculateCustomPhysics.BindUObject(this, &UPhysMeshComponent::SubstepTick);
}

void UPhysMeshComponent::BeginPlay()
{
 Super::BeginPlay();


 //Get the BodyInstance PxRigidbody
 PActorHandle = &GetBodyInstance()->GetPhysicsActorHandle();


}

void UPhysMeshComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
 Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

 if (IsSimulatingPhysics()) {
 //Adding the delegate to the substep
 GetBodyInstance()->AddCustomPhysics(OnCalculateCustomPhysics);
 }
}



void UPhysMeshComponent::SubstepTick(float DeltaTime, FBodyInstance* BodyInstance)
{

}



FVector UPhysMeshComponent::GetLocation() {
 PxTransform PTransform = PActorHandle->SyncActor->getGlobalPose();
 return FVector(PTransform.p.x, PTransform.p.y, PTransform.p.z);
}

FRotator UPhysMeshComponent::GetRotation() {
 PxTransform PTransform = PActorHandle->SyncActor->getGlobalPose();
 return FRotator( FQuat(PTransform.q.x, PTransform.q.y, PTransform.q.z, PTransform.q.w));
}

FVector UPhysMeshComponent::GetLinearVelocity() {
 return FPhysicsInterface::GetLinearVelocity_AssumesLocked(*PActorHandle);
}

FVector UPhysMeshComponent::GetAngularVelocity() {
 FVector PAngularVel = FPhysicsInterface::GetAngularVelocity_AssumesLocked(*PActorHandle);
 return FMath::RadiansToDegrees(PAngularVel);
}





If we want to our component to appear in the add component menu to add them directly to existing actors or to blueprints we just need to use the "BlueprintSpawnableCOmponent" data specifier in the class macro. I also added the "ClassGroup = YourClassGroup" class specifier to keep things organized!
*ClassSpecifiers

UCLASS(ClassGroup = PhysicComponent, meta = (BlueprintSpawnableComponent))

So far we have a fresh new component that ticks as regular but also in the physics substep! Remember that for the substep tick we need to activate substeping in the proyect (ProjectSettings->Physics->Framerate) settings and simulate physics in the component(Physics->SimulatePhysics).



Testing the substepTick

Lets compare the non substep, substep and engine physics approach. We are gonna compare how fall speed behaves in this three cases according to framerate. To do this lets simulate the gravity manually and add a bool to use substep or the regular tick. I also added a log to see in the output log window how many times the substep is executed compared with the regular tick (More info on how to debug).

So we add in our .h file

 UPROPERTY(EditAnywhere)
  bool bUseDefaultTick = false;

And this is how this .cpp functions look

 

void UPhysMeshComponent::BeginPlay()
{
 Super::BeginPlay();

 
 //Get the BodyInstance PxRigidbody
 PActorHandle = &GetBodyInstance()->GetPhysicsActorHandle();

 //Add forces before physics simulation
 SetTickGroup(ETickingGroup::TG_PrePhysics);


}
void UPhysMeshComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
 Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

 if (IsSimulatingPhysics()) {

 //Use the default tick?
 if (bUseDefaultTick) {
  FVector f = FVector(0.0f, 0.0f, -980.0f);
  //Multiplied by delta time by default!
  AddForce(f*GetMass());
  UE_LOG(LogTemp, Warning, TEXT("Default tick"));
 }
 else {
  //Adding the delegate to the substep
  GetBodyInstance()->AddCustomPhysics(OnCalculateCustomPhysics);
 }
 }

}

void UPhysMeshComponent::SubstepTick(float DeltaTime, FBodyInstance* InBodyInstance)
{
 FVector f = FVector(0.0f, 0.0f, -980.0f) * DeltaTime * GetMass();

 FPhysicsInterface::AddForce_AssumesLocked( *PActorHandle, f);

 UE_LOG(LogTemp, Warning, TEXT("Substep tick"));

}



Now if we add our component to a empty actor, and assign a mesh. We sould be able to activate simulate physics and see how the mesh falls. If you deactivate the substep in the project settings the substepTick will be executed just as the regular tick, so dont worry about having to implement everything in the regular tick in case you decide to not use substepping.

Also, if you want to measure function performance, you can do  it with the following:

 double start = FPlatformTime::Seconds();

 // put code you want to time here.

 double end = FPlatformTime::Seconds();
 UE_LOG(LogTemp, Warning, TEXT("code executed in %f seconds."), end-start);


This is all for now, remember to keep those physics under control and program all kind of cool things!

References:


Substepping:
http://www.aclockworkberry.com/unreal-engine-substepping/
https://avilapa.github.io/post/framerate-independent-physics-in-ue4/

4.21 update:
https://forums.unrealengine.com/development-discussion/c-gameplay-programming/1549521-c-transition-guide-for-4-21

Timer:
https://forums.unrealengine.com/development-discussion/c-gameplay-programming/50376-measure-time-a-function-has-taken?p=795851#post795851

Comments

  1. I'm trying your component but there is one point that worries me.

    I activated Substepping, MaxSubstep Delta Time = 0.008333,
    MaxSubstep = 12

    When I play the game at 120 frames per second, it generates 241 physics runs in one second.

    When I play the game at 20 frames per second (which is 6 times less), it only generates 140 physics runs in one second.

    What I would like to build: is that no matter the frame rate, the physical execution time step stabilizes at 120.

    Isn't that what you were suggesting through this article? Is there something I missed?

    ReplyDelete
  2. It's very strange, it looks like the engine has evolved since your article.

    I did a lot of tests, and it seems that once the option is enabled and configured as I did:
    - MaxSubstep Delta Time = 0.008333,
    - MaxSubstep = 12
    The physics is executed according to this time step in the whole scene regardless of the rendering.

    What's still even stranger is that even artificial physics (addForce, addImpulse etc) seem to be executed at a fixed physic time step despite being called through ticks in blueprint.

    This is both a good thing but it bothers me because I need to be able to access this physical time step for my game. I need to be able to know the physical frame number at all times and to be able to accelerate and slow down its execution on demand.

    Do you know how to do access the time step execution (for exemple to print something in log at the same rate as physic) ?

    ReplyDelete

  3. I made a mistake. It works as described above for addForce but not for addImpulse.

    ReplyDelete
    Replies
    1. Keep in mind that AddImpulse is a direct velocity change and it depends on framerate.

      As for access the time step execution, in the substep tick is straightforward, just write whatever code in the function like in the end post example.

      For the usual physics tick, you just need to change the tick group of the component or actor.

      Delete

Post a Comment