Timebased movement and acceleration

Started by
21 comments, last by kloffy 16 years, 3 months ago
Hello, I've got a little problem here. My timebased movement works with "Dead Reckoning"-like methods, just like this:

int Object::getX(int time)
{
    return x + xDir * speed * (time - lastTime);
}
Now, I wanted to add the possibility of acceleration. For that, I just decided to add a variable called "accel" which defines the time in milliseconds which will be needed until the desired speed is reached. So, if it's 1000, the object will move in its normal speed after one second. So I did the following:

int Object::getX(int time)
{
    float timeDiff = time - lastTime;
    float newSpeed;

    if (timeDiff < accel)
        newSpeed = this->speed * (timediff / accel)
    else
        newSpeed = this->speed;

    return x + xDir * newSpeed * timeDiff;
}
And at first it seems to work, but it doesn't. The object accelerates, but as soon as the critical point is reached (i.e. timeDiff is > accel), the object moves slower than before. It is the correct top speed, of course, but short before that point it is moving too fast. How can this be fixed? I'm not too good at mathematics and physics :/
Advertisement
In physics you'll learn that the basic equations for kinematic motion are like this:

velocity = velocity + accel*t;
x = x + velocity*t

This makes a simple Euler integrator. If you want to only accelerate to a certain speed, max_speed, or whatever, then just say velocity = min(max_speed, velocity + accel*t);
Hmm, but if I keep adding up (accel * t) to velocity each time someone calls getX(), wouldn't it then be very different depending on the framerate?

Or do you mean delta t instead of t?
Normally you would integrate once per frame (or take multiple sub-steps once per frame). Generally, the getX( ) method would just return the cached/pre-computed X value.
Acceleration can be, for example expressed as: meters / ( seconds * seconds).
If you want speed the formula is meters / second.
Hence, just multiply the acceleration with the time and you got it.
The normal formula for acceleration is:

v = v0 + a * dt

So:
newSpeed = this->speed + accel * timeDiff;
You don't need complicated integration to calculate x:

x = x0 + v*(t-t0) + a/2*(t-t0)^2

Where t is the current time and t0 is the time of the beginning of the movement. No time deltas anywhere.
Well there's one thing that is important to think of, I'm NOT calculating the position from the last frame's position!

I'm calculating from the time where the last direction change happened. This is why I said it's like the "Dead Reckoning" method, I've got an old position and its corresponding timestamp, and from there, I calculate the new position for every next frame, and the old position and the old timestamp will only change as soon as the direction also changes (or the player stops walking etc).

Let's take an example. The initial X position is 100, a frame takes 20 ms, and accel is 100 ms, which should mean that after 5 frames, the top speed (30) should be reached.

frame 0:
timediff = 0
newSpeed = 30 * (0 / 100) = 0
x = 100 + 1 * 0 * 0 = 100

frame 1:
timediff = 20
newSpeed = 30 * (20 / 100) = 6
x = 100 + 1 * 6 * 20 = 220

frame 2:
timediff = 40
newSpeed = 30 * (40 / 100) = 12
x = 100 + 1 * 12 * 40 = 580

frame 3:
timediff = 60
newSpeed = 30 * (60 / 100) = 18
x = 100 + 1 * 18 * 60 = 1180

frame 4:
timediff = 80
newSpeed = 30 * (80 / 100) = 24
x = 100 + 1 * 24 * 80 = 2020

frame 5:
timediff = 100
newSpeed = 30 * (100 / 100) = 30
x = 100 + 1 * 30 * 100 = 3100

---

frame 6:
timediff = 120
newSpeed = 30 * 1 = 30 ; this time, speed is max_speed
x = 100 + 1 * 30 * 120 = 3700

Now, as you can see, the difference for X between frame 4 and 5 is over 1000, but to frame 6 it's only 600, and that is the reason for the speed "loss". But I just don't know how to make a real, working integral version of my "Dead Reckoning"-like getX()-function.
There is something missing from your design requirement. Does the velocity stop changing only when it becomes >= desired velocity? If this is the case, you initial velocity must always be < your desired velocity and accel value is always positive, am i correct?

If my assumptions are correct, maybe you can do this using euler integration:

void update( delta_time){  // update only velocity when needed  if ( velocity <  desired_velocity ) velocity += accel * delta_time;  else velocity = desired_velocity   // update object's position with respect to velocity  position += velocity * delta_time;}


delta_time is the time spent on last game loop. i hope this helps.
If what you want is to go from 0 to 30 meters/second in 5 frames (100 milliseconds), then your acceleration is 30/100 = 0.3 meters/second^2.

You have this in your newSpeed computation in the example you gave, just written as a ratio of current time to final time instead of an acceleration. So "accel" in your code should really be "tf" (final time). For example in frame 1 you have
newSpeed = 30 * (20 / 100) = 6
which is the same as
newSpeed = (30/100) * 20 = 6

This is as others have shown v = a*t + v0
Where you are computing the speed since t=0 and v0=0.

But your example doesn't match your code:
newSpeed = this->speed * (timediff / accel)
Unless you aren't updating this->speed ever, and it's always 30?
If that's so, then you need to change to update this->speed based on newSpeed somewhere.

What are xDir and lastTime?
Tadd- WarbleWare
You're right that the integration will come out differently depending on your framerate, which is why its common to use a fixed time-step in physics calculations. The basic idea is like this:

dt += time - lastTime;const float timeStep = 1.0f/60.0f;while (dt > timeStep){     // Do physics stuff     ...     dt -= timeStep;}

This topic is closed to new replies.

Advertisement