2
votes

I am trying to plan for a game I started coding. (Very much in the beginnings)

My problem is that I want the acceleration / movement portion of all game objects to be based on acceleration force vs drag (thus resulting in terminal velocity as the upper limit of speed available)

While I could go another route, I'd rather not if possible. Additionally, it has been suggested (by a friend) that I could use a physics library, but that seems overkill and besides, I'd like to learn and understand these concepts myself - I always feel like I better understand my own programs when I do.

I am making a 2D game and using Vector2 variables for position, heading and thrust force (the acceleration force applied). It's top-down so gravity is not a part of the equation.

Before I code it, I'm working out test cases in Excel - which is how I check my math before committing math to code. And I'm discovering that my use of the drag equation is making the object in question framerate dependent!! Specifically, the better the framerate, the lower the resultant terminal velocity.

I've been trying to modify the equations as necessary to account for framerate, but it eludes me.

If you want to work with the same spreadsheet I am, you can download the spreadsheet here.

But you don't have to - here are the specifics.

The drag equation as I understand it is:

Drag = 0.5 * FluidDensity * Velocity * Velocity * DragCoefficient * IncidenceArea

Using some numbers picked from thin air for calculations, if Fluid Density is 0.233 and the Drag Coefficient is 0.4 and the Incidental Area is 0.1 and the Acceleration force is 50 pixels per second, then here is what happens:

If I calculate that acceleration is applied every 0.25 seconds (once every quarter second) at 1/4 the Acceleration force (to match the timing) then we reach terminal velocity at about 39.3 pixels per second.

If I calculate acceleration instead at every second, we reach terminal velocity at about 53.6 pixels per second.

Specifically, every time I calculate for a given DeltaTime, the resultant speed is calculated as (code is from my head - not from an IDE - apologies if there's a bug in it):

//In globals / initialization:
Vector2 Position;
Vector2 Speed;
Vector2 ThrustForce;
float Density = 0.233f;
float DragCoefficient = 0.4f;
float IncidentalArea = 0.1f;

//In the update loop
//DeltaTime is a float based upon how much of a second passed
Vector2 AccelerationToApply = ThrustForce * DeltaTime;
Vector2 NewSpeed = Speed + AccelerationToApply;
Vector2 Drag = Speed * Speed * 0.5f * Density * DragCoefficient * IncidentalArea;
NewSpeed -= Drag;
Speed = NewSpeed;

That's the problem math. Here is the question:

How should this be expressed so that it's framerate independent?

2
Drag is a force like the ThrustForce, yet you do not multiply it by DeltaTime. That smells like a bug.Joren
Actually I've tried that too (which is the joy of doing pre-work in Excel - I can make rapid modifications and see the result) and it still gives vastly different results.Dracorat
I've been playing with Excel all day and finally arrived at the right answer. Your answer was actually half the solution so submit it as the answer and I'll accept it. In the mean time, I'm going to update the original post and add the full answer.Dracorat
Please don't put your solution into the body of the question. Create and and accept your own answer.Kev
Ok, no problem - I'll do so now.Dracorat

2 Answers

3
votes

The classic approach is to step the simulated physical time independent from the game loop frame rate, calculating multiple sub-iterations per frame if necessary to advance the physics. This allows you to control your time step (generally making it smaller than the main frame rate), which also helps to keep other potentially unstable calculations under control (such as oscillators.) This of course means that your physics has to compute faster than real time for the fixed time step chosen, as otherwise your world goes into slow motion.

Speaking of instability, I imagine that you'll see some oscillation effects in your current implementation, depending on whether you're overshooting the terminal velocity in a given time step. One way to resolve this is to compute the speed via analytical integration instead of approximating using a incremental step. To do that, express your formula as a differential equation and see if it is of a form that can be readily solved analytically.

1
votes

There were two parts missing from the code above. While I had played with turning one part "on" and "off" to experimentally determine if it was needed, without the other I was having problems finding the right answer.

The two parts are this: The resultant drag does need to be multiplied by the time step in order to reduce its effect upon the acceleration, but also and perhaps more importantly - the acceleration force to be applied on this frame needs the drag subtracted from it before it is applied to the speed - not after like I had above.

The modified (and now framerate independent) code looks like this: Also, I reduced having 4 "constant" coefficients to just one coefficient for the sake of simplicity.

//In globals / initialization:
Vector2 Position;
Vector2 Speed;
Vector2 ThrustForce;
float Coefficient = 0.009f;
float PreviousDrag = 0.000f;

//In the update loop
//DeltaTime is a float based upon how much of a second passed
Vector2 AccelerationToApply = ThrustForce * DeltaTime + PreviousDrag * DeltaTime;
Vector2 NewSpeed = Speed + AccelerationToApply;
PreviousDrag = Coefficient * NewSpeed * NewSpeed;
Speed = NewSpeed;

Running this logic through excel, I find that at approximately the same times I reach the same approximate terminal velocity no matter how often (or not) I calculate a change in velocity.