Using a Fixed Timestep for the Physics Engine
Hi, I’ve wanted to implement a for my game that is not tied to my game framerate, because of a few improvements it has for the kind of physics I needed.
Normally the physics engine is tied to the actual game framerate (up to a specific framerate, in which the physics simulation starts to run slower). That means that if your framerate is lower, you will have a longer tick in the physics engine as well. The longer the ticks are, the more inaccurate the physics engine is, and the more it has to guess. This causes at very low framerates problems like two things hitting together and flying suddenly at the speed of sound, etc. Now, Unreal has a solution for this, there is the physics substepping which can, if your framerate is low, divide a single game tick into a few physics ticks, so that effectively fixes that problem.
The main reason I’ve wanted to use a fixed timestamp is to have very consistent physics that I can almost call deterministic. If the Delta Time between each physics frame is always the same, then the two identical physics simulations will give the same results, unlike non-fixed timestamps, which have differing framerates that will bring different results each time. There is still probably a bit of variance because of floating point inaccuracies, but it’s probably so small that I’ve noticed nothing of the sort.
This also gives you full control over your physics framerate, more than you would have even with substepping.
If it’s so good, why didn’t Epic implement this themselves?
As Epic Developer says, they debated this internally, wether to use a fixed timestamp, or a semi-fixed timestamp like they use today, they decided to use a semi-fixed timestamps for a number of reasons.
-
With a fixed timestep, since the physics tick is different than the game tick, this can introduce a latency of up to 1 physics frames. For example, if the game runs at 60fps, and the physics runs at 75fps, for each game frame, the physics must process 1.25 ticks per game tick. But you can’t process a quarter tick, so what it does it is keep the extra time in a timebank that once it goes over 1 frame, you process that frame as well, so that means your physics simulation is in the past anywhere from 0 frames to less than 1 frame. This is probably negligible unless you are running at a very low physics framerate. I’ve also heard this can introduce some weird visual artifact but I’ve experienced nothing of the sort.
-
With a fixed timestep, you can’t use the game DeltaTime on the tick function for physics operations, since that DeltaTime is different than the physics DeltaTime. What you need to do instead is use the FCalculateCustomPhysics delegate instead of your tick function. Example Here.
There may be more drawbacks that I do now know about, but for me its worth these two.
I also do not know what happens if the physics engine can’t do all those physics ticks in the time its allocated.
How to implement this:
First of all, this uses the Unreal Substepping system. So for this to work you need to go to your Physics settings (Edit->Project Settings->Physics) and enable the Substepping option. Also, to disable this fixed timestep, you can just disable the substepping and go back to the normal system.
Open your Engine code project (can’t use a binary build, need to change the engine for this), and go to a file called PhysSubstepTasks.h, there go into the implementation of FPhysSubstepTask::UpdateTime.
This method gets called every frame and receives a UseDelta value, which is the game tick DeltaTime, and sets the amount of substeps required to be done, and the DeltaTime for these substeps. Just comment everything in it, and replace it with what were gonna do below.
What you want to do is add to the FPhysSubstepTask class a float value, called ExtraTimeBank in my case, which will store the extra that needs to be calculated. Initialize it to 0 in the constructor.
In the UpdateTime method you want to have a float value that will signify the frame rate the physics will run at, I just used the MaxSubstepDeltaTime value from the physics options so I could change it from the editor.
float FPhysSubstepTask::UpdateTime(float UseDelta)
{
UPhysicsSettings * PhysSetting = UPhysicsSettings::Get();
float FrameRate = PhysSetting->MaxSubstepDeltaTime;
After that divide the UseDelta by the FrameRate, and floor that to find the amount of frames we want to calculate now, and the value past the point on it is the amount of frames we want to add to the timebank
float ExtraSteps = UseDelta/ FrameRate;
uint32 AmountOfSubsteps = FMath::FloorToInt(ExtraSteps);
ExtraSteps -= (float)AmountOfSubsteps;
//Add to the time bank
ExtraTimeBank += ExtraSteps * FrameRate;
And then check if the timebank has frames that can be filled in it, if so, take it.
uint32 TryGetExtraFrames = FMath::FloorToInt(ExtraTimeBank / FrameRate);
ExtraTimeBank -= (float)TryGetExtraFrames * FrameRate;
AmountOfSubsteps += (float)TryGetExtraFrames;
Then set the class’ NumSubstep and DeltaSeconds to the values you got, and return the division of them as the function requires.
NumSubsteps = AmountOfSubsteps;
DeltaSeconds = FrameRate * AmountOfSubsteps;
SubTime = DeltaSeconds / NumSubsteps;
return SubTime;
Compile the engine, and now when you start it, with Substepping enabled you can have a fixed timestep for your physics engine.
Change the framerate of the physics engine by changing the Max Substep Delta Time in your Physics settings (0.016666 is 60fps etc)
If you want to fast forward the physics engine for some reason, just increase the UseDelta time the function receives by the amount of time you want to fast forward. The UpdateTime method gets called from FPhysScene::SubstepSimulation.
I thank 0lento and OmaManfred for helping me find all this out.