views:

1192

answers:

6

Hi.

I'm working on a 2D game where I'm trying to accelerate an object to a top speed using some basic physics code.

Here's the pseudocode for it:


const float acceleration = 0.02f;
const float friction     = 0.8f;  // value is always 0.0..1.0
      float velocity     = 0;
      float position     = 0;

move()
{
   velocity += acceleration;
   velocity *= friction;
   position += velocity;
}

This is a very simplified approach that doesn't rely on mass or actual friction (the in-code friction is just a generic force acting against movement). It works well as the "velocity *= friction;" part keeps the velocity from going past a certain point. However, it's this top speed and its relationship to the acceleration and friction where I'm a bit lost.

What I'd like to do is set a top speed, and the amount of time it takes to reach it, then use them to derive the acceleration and friction values.

i.e.,


const float max_velocity = 2.0; 
const int   ticks;       = 120; // If my game runs at 60 FPS, I'd like a 
                                // moving object to reach max_velocity in 
                                // exactly 2 seconds.
const float acceleration = ?
const float friction     = ?
+2  A: 
velocity *= friction;

This doesn't prevent the velocity from going about a certain point...

Friction increases exponentially (don't quote me on that) as the velocity increases, and will be 0 at rest. Eventually, you will reach a point where friction = acceleration.

So you want something like this:

velocity += (acceleration - friction);
position += velocity;
friction = a*exp(b*velocity);

Where you pick values for a and b. b will control how long it takes to reach top speed, and a will control how abruptly the friction increases. (Again, don't do your own research on this- I'm going from what I remember from grade 12 physics.)

I initially forgot to mention that the friction should be between 0.0..1.0. Anyway, even though the above code increases the velocity using steps, I don't see how a maximum velocity is referenced by "b".
Radek
+15  A: 

HEAVY EDITS

I found this question very interesting since I had recently done some work on modeling projectile motion with drag.

Point 1: You are essentially updating the position and velocity using an explicit/forward Euler iteration where each new value for the states should be a function of the old values. In such a case, you should be updating the position first, then updating the velocity.

Point 2: There are more realistic physics models for the effect of drag friction. One model (suggested by Adam Liss) involves a drag force that is proportional to the velocity (known as Stokes' drag, which generally applies to low velocity situations). The one I previously suggested involves a drag force that is proportional to the square of the velocity (known as quadratic drag, which generally applies to high velocity situations). I'll address each one with regard to how you would deduce formulas for the maximum velocity and the time required to effectively reach the maximum velocity. I'll forego the complete derivations since they are rather involved.


Stokes' drag:

The equation for updating the velocity would be:

velocity += acceleration - friction*velocity

which represents the following differential equation:

dv/dt = a - f*v

Using the first entry in this integral table, we can find the solution (assuming v = 0 at t = 0):

v = (a/f) - (a/f)*exp(-f*t)

The maximum (i.e. terminal) velocity occurs when t >> 0, so that the second term in the equation is very close to zero and:

v_max = a/f

Regarding the time needed to reach the maximum velocity, note that the equation never truly reaches it, but instead asymptotes towards it. However, when the argument of the exponential equals -5, the velocity is around 98% of the maximum velocity, probably close enough to consider it equal. You can then approximate the time to maximum velocity as:

t_max = 5/f

You can then use these two equations to solve for f and a given a desired vmax and tmax.


Quadratic drag:

The equation for updating the velocity would be:

velocity += acceleration - friction*velocity*velocity

which represents the following differential equation:

dv/dt = a - f*v^2

Using the first entry in this integral table, we can find the solution (assuming v = 0 at t = 0):

v = sqrt(a/f)*(exp(2*sqrt(a*f)*t) - 1)/(exp(2*sqrt(a*f)*t) + 1)

The maximum (i.e. terminal) velocity occurs when t >> 0, so that the exponential terms are much greater than 1 and the equation approaches:

v_max = sqrt(a/f)

Regarding the time needed to reach the maximum velocity, note that the equation never truly reaches it, but instead asymptotes towards it. However, when the argument of the exponential equals 5, the velocity is around 99% of the maximum velocity, probably close enough to consider it equal. You can then approximate the time to maximum velocity as:

t_max = 2.5/sqrt(a*f)

which is also equivalent to:

t_max = 2.5/(f*v_max)

For a desired vmax and tmax, the second equation for tmax will tell you what f should be, and then you can plug that in to the equation for vmax to get the value for a.


This seems like a bit of overkill, but these are actually some of the simplest ways to model drag! Anyone who really wants to see the integration steps can shoot me an email and I'll send them to you. They are a bit too involved to type here.

Another Point: I didn't immediately realize this, but the updating of the velocity is not necessary anymore if you instead use the formulas I derived for v(t). If you are simply modeling acceleration from rest, and you are keeping track of the time since the acceleration began, the code would look something like:

position += velocity_function(timeSinceStart)

where "velocity_function" is one of the two formulas for v(t) and you would no longer need a velocity variable. In general, there is a trade-off here: calculating v(t) may be more computationally expensive than simply updating velocity with an iterative scheme (due to the exponential terms), but it is guaranteed to remain stable and bounded. Under certain conditions (like trying to get a very short tmax), the iteration can become unstable and blow-up, a common problem with the forward Euler method. However, maintaining limits on the variables (like 0 < f < 1), should prevent these instabilities.

In addition, if you're feeling somewhat masochistic, you may be able to integrate the formula for v(t) to get a closed form solution for p(t), thus foregoing the need for a Newton iteration altogether. I'll leave this for others to attempt. =)

gnovice
+1 for not getting dragged into the discrete-math rathole I seem to have created. I like your simplicity, though I _really_ want to answer the OP's request to specify time and max velocity! BTW, great handle!
Adam Liss
Thanks! I may have avoided a discrete-math rathole, but I fell right into an integration crevasse! =)
gnovice
Thanks as well! That was quite a bit more complicated than I expected, but the end-result approximations are very close and work quite well.
Radek
Beautiful job! After another hour playing with the discrete math, infinite series, and simultaneous equations, I've convinced myself that a linear or exponential decay of acceleration is both simple and reasonably accurate. BTW, formatting is harder than the math!
Adam Liss
Yeah, formatting the equations to look good was tough, so I just wrote them as they would likely appear in code. There must be some way to make nicely formatted TeX-like equations, but I have yet to figure it out. =)
gnovice
+3  A: 

Warning: Partial Solution

If we follow the physics as stated, there is no maximum velocity. From a purely physical viewpoint, you've fixed the acceleration at a constant value, which means the velocity is always increasing.

As an alternative, consider the two forces acting on your object:

  • The constant external force, F, that tends to accelerate it, and
  • The force of drag, d, which is proportional to the velocity and tends to slow it down.

So the velocity at iteration n becomes: vn = v0 + n F - dvn-1

You've asked to choose the maximum velocity, vnmax, that occurs at iteration nmax.

Note that the problem is under-constrained; that is, F and d are related, so you can arbitrarily choose a value for one of them, then calculate the other.

Now that the ball's rolling, is anyone willing to pick up the math?

Warning: it's ugly and involves power series!


Edit: Why doe the sequence n**F** in the first equation appear literally unless there's a space after the n?

Adam Liss
+1 for the partial solution, although I think I can get around the power series with some closed-form integrals to show how to choose the max velocity and time to max velocity.
gnovice
+1  A: 

If you want to see what can be done with very simple physics models using very simple maths, take a look at some of the Scratch projects at http://scratch.mit.edu/ - you may get some useful ideas & you'll certainly have fun.

anon
+1  A: 

This is probably not what you are looking for but depending on what engine you are working on, it might be better to use a engine built by some one else, like farseer(for C#). Note Codeplex is down for maintenance.

Diones
+1  A: 

This isn't answering your question, but one thing you shouldn't do in simulations like this is depend on a fixed frame rate. Calculate the time since the last update, and use the delta-T in your equations. Something like:

static double lastUpdate=0;
if (lastUpdate!=0) {
  deltaT = time() - lastUpdate;
  velocity += acceleration * deltaT;
  position += velocity * deltaT;
}
lastUpdate = time();

It's also good to check if you lose focus and stop updating, and when you gain focus set lastUpdate to 0. That way you don't get a huge deltaT to process when you get back.

GoatRider
For this particular implementation, the fixed-frame isn't really an issue as there's not that many calculations to deal with and I'm not working with a limited -- i.e., slow -- device. But yes, otherwise the delta time would definitely be part of the equation.
Radek
Set deltaT = time() - lastUpdate, unless you're looking in a mirror. :-)
Adam Liss
Doh, you're right. I typed too quickly there. I'll fix it.
GoatRider