Fixed timestep update for physics / damping code?

Hello.

I have a Component in which I need to run some physics related code. Specifically, linear damping but only on the X and Y axis and not Z.

I currently run the following code in the TickComponent():

	UPrimitiveComponent* component = PawnOwner->GetRootPrimitiveComponent();
	FVector initVel = component->GetPhysicsLinearVelocity();
	FVector hInitVel = FVector(initVel.X, initVel.Y, 0);
	component->SetPhysicsLinearVelocity(initVel - hInitVel * damping * DeltaTime);

But it is not framerate independent.

Is there a way I can run the above code at a fixed timestep?

Thanks.

I think somethings wrong with your calculations, you scaling whole result where what you should just scale the delta values, so try this

initVel - ((hInitVel * damping) * DeltaTime)

It doesn’t look like those parentheses are changing the order of operations at all. Multiplication is commutative, so order doesn’t matter. And the multiplication happens before subtraction whether you have parentheses or not. Unless I’m missing something, wouldn’t both of those give the same results?

I think to get a fixed timestep, you would just wrap your damping code in a loop and use a static variable to track the elapsed time. It would look something like this (desiredTimeStep would be whatever fixed duration you want):

static float elapsedTime = 0.0f;

elapsedTime += DeltaTime;
while (elapsedTime >= desiredTimeStep)
{
  elapsedTime -= desiredTimeStep;
  FVector initVel = component->GetPhysicsLinearVelocity();
  FVector hInitVel = FVector(initVel.X, initVel.Y, 0);
  component->SetPhysicsLinearVelocity(initVel - hInitVel * damping * desiredTimeStep);
}

Looking at your code again, I guess I probably wouldn’t use a static variable like in the example code I have. Maybe make that a member variable.

If you know for sure that you’re only going to have one object in the game running that code, the static variable will work fine. If you’re going to have multiple objects, the static variable is persistent across all of them and you’d get some undesired behavior as a result.

Yeah, I would use a member for that.

Wouldn’t this solution only have an effect if the desiredTimeStep is a significantly greater than the average DeltaTime, through reducing the amount of iterations (and accuracy)?

I think the issue is that, while you can scale the velocity reduction by DeltaTime, the distance moved won’t actually be the same due to physics being stepped at different intervals to rendered frames. (Or at more iterations)

9678-veltime.png

So, for a classic Velocity / Time graph where the area under the graph (roughly) represents the distance moved, you can see that while the velocities of two different framerates will always be similar, the distance moved won’t be.

I need a way for the distance moved to be the same.

I think I need either a way to tick at a small fixed timestep, or somewhere I can run code after each physics step iteration.

It shouldn’t unless I wrote something out incorrectly. It just forces the damping part of your update to use the exact same step value each time. If desiredTimeStep is significantly smaller than the average DeltaTime, you’d be running that while loop a bunch of times each frame with smaller delta values.

Quick examples with completely arbitrary numbers:

Let’s say your average DeltaTime is 10 and your desiredTimeStep is 5. Each frame you’d dampen your velocity 2 times using your desired “5” for each iteration. If you have some really fast frames back to back and the DeltaTimes are 1 each, you don’t dampen at all until it accumulates to 5, and then you use 5 again the next frame. It also maintains its consistency by carrying over the leftover time so you aren’t dropping anything. If you get a frame with a DeltaTime of 12, you’d carry 2 of those over and they’d get rolled into the next frame’s time.

Each dampening will be completely consistent whether your actual framerate is faster or slower than “desired”.

EDIT: I changed the code I posted above. I used the wrong variable for the actual damping. It was supposed to be desiredTimeStep and not elapsedTime. That would have been completely wrong. Fixed now though.

Thanks, but unfortunately this doesn’t really do anything if the desiredTimeStep < deltaTime apart from forcing the velocity to only update in multiples of desiredTimeStep. It still only updates at variable deltaTime intervals, with engine physics still using the velocity in between those updates, thus giving different results for different deltaTime values.

I posted an image as a comment to my OP which illustrates what I think the problem is, I hope.

shrug That’s what a fixed time step is. The question said you wanted a fixed time step for linear damping. If you want it to do that for the velocity as well, then turn off physics and handle velocity in the same loop.

Here’s an article for more information:

And just to clarify, the step size makes a difference. If you’re desired time lapse is based on 60 frames per second and your game is running at 30 frames per second, it makes a pretty big difference. The velocity after only 2 seconds in that case can be off by 1.5 - 2%. Obviously with longer periods of time, that difference grows.

And the Unreal Engine 4 page talking about how they do fixed time steps:

Well, damping is just altering velocity…

Sadly I can’t just disable physics and handle it myself as I need the actor to respond physically to collisions with other actors and also to AddForce and AddImpulse and such. (If I could, life would be MUCH easier for me…)

I have indeed seen Fix Your Timestep and know about the built in substepping, and yes they are very relevant :slight_smile:

Looking at the substepping article again, I suppose what I’m looking for is a way to handle custom damping (and/or any other physics related code) in the physics thread, as if possible, would be a way to handle it independently of framerate, and without having to disable the engine physics and write my own.

Although at the end of the day here, all I’m really trying to do is apply linear damping on 2 axis rather than 3. It doesn’t seem like it would be impossible to do with the engine physics, but it may well be.

Something like a PostPhysicsStep(float deltaTime) would be amazing…

Does the physics system already do linear damping? I haven’t done anything with physics in UE4, but it seems like it would. If it does, why not just store the z-value for the velocity each frame. The next frame, just set the z-velocity to the previous frame’s value. You’d be storing a single float and doing an assignment each frame instead of the more involved calculations.

Indeed it does, and it’s a good idea however locking the z-velocity through the physics calculations I think would also disable gravity, impulse from collisions, etc on that axis…

Unreal Engine does not use fixed time steps, and cannot get deterministic behavior using the default physics integration. The best you can do is enable sub-stepping, setting some small sub-step size, and hope for the best. I have a simulation that definitely “hitches” when something takes away the machine for a few milliseconds :frowning:

To provide consistent (but still not 100% deterministic) behavior with variable time step, you’d have to use a Runge-Kutta-4 integrator, which is way too expensive (calculating all those derivatives) and also use swept collision detection across the board.

Yeah, I’d not expect it to be completely deterministic, but I’m getting an almost consistent 5% offset in my above situation, which seems pretty huge.

Let’s assume the following code gives the desired result with a fixed 1/60s timestep:

component->SetPhysicsLinearVelocity(initVel - hInitVel * damping * DeltaTime);

This effectively multiplies the X and Y components each frame by:

(1 - damping / 60)

After N 60fps steps, the X and Y components have been multiplied by:

(1 - damping / 60) ^ N

So what if DeltaTime is not constant? No problem, you can treat DeltaTime as a non-integer number of 60fps steps:

(1 - damping / 60) ^ (DeltaTime * 60)

This gives you the following, framerate-independent code:

float const TimedDamping = 1.f - FMath::Pow(1.f - damping / 60, DeltaTime * 60);
component->SetPhysicsLinearVelocity(initVel - hInitVel * TimedDamping);