Very simple physics problem

Oldtimer
Posts: 834
Joined: 2002.09
Post: #1
I'm going nuts! My world is turning upside down!

What I though was the simplest physics code in the known world, proves to be not.

It's simple. I want to implement gravity. So, I declare a class which has position, velocity and acceleration. I set the acceleration to be constant, right? Then, for each frame, I add the acceleration * (time step), right? This gives me correct velocity:

double ts = myClock->GetTimeStep();
vel += acc*ts;

Right? Now, I figured, to add this to my model's position, I just add the velocity, times the time step, right?

pos += vel*ts;

Now, this proves to be ridicoulously incorrect. What the heck am I doing wrong? It looks OK, but when I move this code to a slower computer, everything moves hellishly wrong. It seems that as the time-step grows larger, (lower frame-rate), gravity seems to fade.

Now, the way I figure it, this iterative formula would translate quite nicely into pos = v*t; v = a * t -> pos = (a*t)*t = at^2, which I think looks pretty familiar... What the heck am I doing wrong? :sorry:
Quote this message in a reply
Hog
Member
Posts: 151
Joined: 2002.09
Post: #2
how about

Code:
pos += vel*ts - acc*ts*ts*.5;
vel += acc*ts;
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #3
I'm guessing your GetTimeStep() method is wrong...
Quote this message in a reply
Oldtimer
Posts: 834
Joined: 2002.09
Post: #4
I briefly looked at c_dev's solution, and it seems to work... but I haven't tough-tested this yet. Thanks, though!

OSC, I' afraid it isn't - it works for everything else, and this error emerges even on paper and in an Excel spreadsheet... Sad But yeah, I went down that road.
Quote this message in a reply
Hog
Member
Posts: 151
Joined: 2002.09
Post: #5
i double checked on that, and i think it shoud be

Code:
pos += vel*ts + acc*ts*ts*.5;
vel += acc*ts;

since D[pos(0) + vel(0)*ts - acc*ts*ts*.5] would be vel(0) - acc*ts

the difference is probably small enough not to be seen
(though actually it would seem to make a grave difference)
Quote this message in a reply
Oldtimer
Posts: 834
Joined: 2002.09
Post: #6
Nope... That didn't do it either. It's funny, I've always held this as a tried and true solution, and finding it fail isn't fun at all.

I hacked up a quick app that tried different algos at different delta intervals, but I haven't found anything that has been consistent over the tests. For instance, c_dev's last attempt (thanks, man!) gives a position of 75 after 2 seconds at a 0.5 second delta, but 25 after two seconds with a 1 second delta. I'm confused beyond everything!

:?:
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #7
I use precisely your original method in Smiley Tag, and that seems to produce reasonable acceleration. Obviously it will be less accurate as the time step increases (you are approximating a curve by a straight line after all).

One possible solution is to run your physics at a fixed (high) rate. For each variable timestep, just run the physics the appropriate number of times:

Code:
void step_system(physics_system *ps, float raw_dt)
{
    float dt = raw_dt + ps->overflow_dt;

    for (; FIXED_TIME_STEP <= dt; dt -= FIXED_TIME_STEP)
    {
        do_physics(ps);
    }
    
    ps->overflow_dt = dt;
}
Quote this message in a reply
Oldtimer
Posts: 834
Joined: 2002.09
Post: #8
Quote:(you are approximating a curve by a straight line after all)


Why, this could be it... But what the hey, is that what every game that implements gravity does? Or any other kind of acceleration - this doesn't seem right to me... >Sad (But of course you are, OSC.) I just get the feeling that there has to be a better way to do this, and get consistent jump heights on different computers - right now, my 733 G4 makes the little guy jump about three times higher than he does on my 233 PowerBook...
Quote this message in a reply
Moderator
Posts: 916
Joined: 2002.10
Post: #9
thanks to this thread, I fixed a long overdue bug in the post udevgames BOB2. Yeah, apparently over time you would slow down horribly. quite a horrendous bug. I fixed it now! Grin and GOD my particles look awesome at high framerates (the higher the framerates, the more particles are created to balance out your super computer) Rasp
Quote this message in a reply
Oldtimer
Posts: 834
Joined: 2002.09
Post: #10
OSC, I just now realized how convinient your way is. Thanks, I'll do it right away!

Skyhawk, sounds amazing! Grin
Quote this message in a reply
DoG
Moderator
Posts: 869
Joined: 2003.01
Post: #11
You cannot expect approximative physics like the above code to return the same results with different time steps.

I suggest you find a time step which small enough for the simulation to work, but still works on the slowest target platform, and artificially limit the timestep to that value.

Or, if simple gravity is all you have, you can derive a formula for the relative error generated as a function of the timestep size, and use that factor to correct your physics.

I suggest the 1st way, as it will give you consistent physics easily, and it works not only for simple gravity.
Quote this message in a reply
Sage
Posts: 1,482
Joined: 2002.09
Post: #12
Yeah, what doog said. You have to use a consistent step size. Most games run their simulation 30 times per second or more, and then the rest of the processor time goes to graphics. You *must* keep the step size the same though, otherwise you have to do some calculus to correct for errors. Simple gravity wouldn't be so hard to do it for, but anything more complicated would get messy really fast.

Scott Lembcke - Howling Moon Software
Author of Chipmunk Physics - A fast and simple rigid body physics library in C.
Quote this message in a reply
Hog
Member
Posts: 151
Joined: 2002.09
Post: #13
Quote:Originally posted by Fenris
...For instance, c_dev's last attempt (thanks, man!) gives a position of 75 after 2 seconds at a 0.5 second delta, but 25 after two seconds with a 1 second delta. I'm confused beyond everything!...

that is strange, if you calculate:

Code:
pos(1) = pos(0) + vel(0)*ts(0) + acc*ts(0)*ts(0)*.5
pos(2) = pos(1) + vel(1)*ts(1) + acc*ts(1)*ts(1)*.5
       = pos(0) + vel(0)*ts(0) + acc*ts(0)*ts(0)*.5 + (vel(0)+acc*ts(1))*ts(2) + acc*ts(1)*ts(1)*.5
       = pos(0) + vel(0)*ts(0) + vel(0)*ts(2) + acc*.5*( ts(0)*ts(0) + 2*ts(0)*ts(1) + ts(1)*ts(1) )
       = pos(0) + vel(0)*( ts(1)+ts(2) ) + acc*.5*( ts(0)+ts(1) )*( ts(0)+ts(1) )

so theoretically is should be the same
Quote this message in a reply
Member
Posts: 304
Joined: 2002.04
Post: #14
Quote:Originally posted by Fenris
Nope... That didn't do it either. It's funny, I've always held this as a tried and true solution, and finding it fail isn't fun at all.

well either you are wrong or Newton and Leibnitz are wrong. I know where Im placing my money...

c_dev used calculus to figure out the formula for the position by integrating the acceleration fomula once to get a velocity formula - and then again to get the position formula.

This produces the *exact* correct answer regardless how many timesteps you use. However there is a problem with this. Its only really useful for the simplest of simulations. Add friction or collision and exact integration becomes undoable. So for most games we are back to using OSC's suggestion to using a fixed timestep for your physics/movement/collision calculations. There are other benefits of using a fixed timestep.

However, I did want to test to make sure that my calc books havent lied to me all these years, so...:

Enter length of simulation in seconds (or 0 to quit): 2.0
Break that time into how many steps?: 8

step # = 0 / 8
step amount = 0.25
time passed = 0
acceleration = -10
position = 0
velocity = 0

step # = 1 / 8
step amount = 0.25
time passed = 0.25
acceleration = -10
position = -0.3125
velocity = -2.5

step # = 2 / 8
step amount = 0.25
time passed = 0.5
acceleration = -10
position = -1.25
velocity = -5

step # = 3 / 8
step amount = 0.25
time passed = 0.75
acceleration = -10
position = -2.8125
velocity = -7.5

step # = 4 / 8
step amount = 0.25
time passed = 1
acceleration = -10
position = -5
velocity = -10

step # = 5 / 8
step amount = 0.25
time passed = 1.25
acceleration = -10
position = -7.8125
velocity = -12.5

step # = 6 / 8
step amount = 0.25
time passed = 1.5
acceleration = -10
position = -11.25
velocity = -15

step # = 7 / 8
step amount = 0.25
time passed = 1.75
acceleration = -10
position = -15.3125
velocity = -17.5

step # = 8 / 8
step amount = 0.25
time passed = 2
acceleration = -10
position = -20
velocity = -20

Enter length of simulation in seconds (or 0 to quit): 2.0
Break that time into how many steps?: 2

step # = 0 / 2
step amount = 1
time passed = 0
acceleration = -10
position = 0
velocity = 0

step # = 1 / 2
step amount = 1
time passed = 1
acceleration = -10
position = -5
velocity = -10

step # = 2 / 2
step amount = 1
time passed = 2
acceleration = -10
position = -20
velocity = -20

Enter length of simulation in seconds (or 0 to quit): 2.0
Break that time into how many steps?: 1

step # = 0 / 1
step amount = 2
time passed = 0
acceleration = -10
position = 0
velocity = 0

step # = 1 / 1
step amount = 2
time passed = 2
acceleration = -10
position = -20
velocity = -20

Enter length of simulation in seconds (or 0 to quit): 0.0

calc_test has exited with status 0.


here is the code:

[SOURCECODE]#include <iostream>

int main (int argc, const char * argv[]) {
while (true)
{
double position, velocity, acceleration, timeStep, timeLength;
int numberOfSteps;

position = velocity = timeStep = timeLength = 0.0;
acceleration = -10.0; //effect of force of gravity
numberOfSteps = 0;

std::cout << "Enter length of simulation in seconds (or 0 to quit): ";
std::cin >> timeLength;
if (0.0==timeLength) return 0; //normally dont use == w/ a float - but w 0.0 its ok
std::cout << "Break that time into how many steps?: ";
std::cin >> numberOfSteps;
timeStep = timeLength / double(numberOfSteps);
for (int count = 0; count <= numberOfSteps; ++count )
{
std::cout << std::endl;
std::cout << "step # = " << count << " / " << numberOfSteps << std::endl;
std::cout << "step amount = " << timeStep << std::endl;
std::cout << "time passed = " << timeStep * double(count) << std::endl;
std::cout << "acceleration = " << acceleration << std::endl;
std::cout << "position = " << position << std::endl;
std::cout << "velocity = " << velocity << std::endl;
// from c_dev's post
// pos += vel*ts + acc*ts*ts*.5;
// vel += acc*ts;
position += velocity*timeStep + acceleration*timeStep*timeStep*0.5;
velocity += acceleration*timeStep;
}
std::cout << std::endl;
}
return 0; //shouldnt get here
}[/SOURCECODE]

cheers,
Codemattic
Quote this message in a reply
Member
Posts: 156
Joined: 2002.10
Post: #15
Quote:Originally posted by Fenris

double ts = myClock->GetTimeStep();
vel += acc*ts;

Right? Now, I figured, to add this to my model's position, I just add the velocity, times the time step, right?

pos += vel*ts;


Another possible way to do this (maybe simpler?) is:

double deltaT = myClock->GetTimeStep();
double oldVel = vel;
vel += acc*deltaT;

pos += (vel+ oldVel)/2*deltaT;

This way you are actually adding on the average velocity over the previous time interval. it is OK to use this simple average as velocity increases linearly with time.

Here are some pen & paper examples:

acc = -10;
deltaT = 0.5;

0.5:
vel = -5
pos = -1.25

1.0:
vel = -10
pos = -5

1.5:
vel = -15
pos = -11.25

2.0:
vel = -20
pos = -20

Another example:

acc = -10
deltaT = 1.0

1.0:
vel = -10
pos = -5

2.0:

vel = -20
pos = -20

I am 100% sure that this works. if it doesn't I'm going to look a bit silly, and I wonder what I actually learnt in the last 6 years of studying Physics Smile

- Iain
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Bullet Physics Library / COLLADA physics viewer erwincoumans 14 12,968 Aug 12, 2006 12:59 AM
Last Post: Fenris
  Simple Physics Game Zenith 8 5,613 Jan 13, 2004 10:01 PM
Last Post: Josh
  Simple physics math LongJumper 8 3,901 Sep 5, 2003 08:23 PM
Last Post: mars