Move an object a percentage every frame ("Zeno's Arrow" style)

78 Views Asked by At

Every frame, I want an object to move a percentage of the way from where it is to its target. A value of 1 means it reaches the target in exactly one frame, and a value of 0.5 means in one frame it has gone 0.5 way to its target, the next frame 0.75, etc.

diagram
(diagram stolen from this similar question)

Since we are moving at fixed frames (not infinitesimals), barring floating-point precision, the object will never reach the target.

The code for this is easy. Here it is in C# pseudocode:

void onEveryFrame() {
    currentX += (targetX - currentX) * speed;
}

The problem is, How do I make this frame-rate independent?

So someone running at 30 frames per second will see the object move at about the same speed as someone running at 60 frames per second?

3

There are 3 best solutions below

5
On

Just divide your desired speed by the framerate:

currentX += (targetX - currentX) * speed / framerate;

You may want to increase the speed constant to compensate for this division. For example, if you've tested this on 30 FPS before, now write a 30 times higher speed. The division by the framerate will cancel this factor out. If you are doing 60 FPS, you will be doing it twice as often, but half as fast. And so on.

11
On

Since your trajectory is so well defined, you can calculate the position for any time $t$. Simply start a timer when your trajectory starts, then every time you want to refresh (it doesn't even have to be a constant frame rate), you can update the position $x$ using $$ x = 1 - \left(\frac{1}{2} \right)^t $$

If you are starting at some non-zero position $x_0$, then your position at time $t$ is just $x_0$ plus the above: $$ x = x_0 + 1 - \left(\frac{1}{2} \right)^t $$ or if you like

x = startX + 1 - (1 / 2^t)

You shouldn't need to multiply by "speed," especially since the speed here is not constant, but exponentially decreasing as the trajectory approaches startX+1

0
On

The answer by Carser gave me almost everything I needed. The following function returns a value from 0 (at he starting position) to 1 (at the target), with $t$ representing the current number of seconds elapsed.

$$ x = 1 - \left(\frac{1}{2} \right)^t $$

There are few things we need to add for the equation to work in our code:

$$ x = x_0 + \Delta x * \left( 1 - \left(1-rate \right)^t \right) $$

In the equation, $x_0$ is our starting value, $\Delta x$ is the total distance to the target (in our case targetX - startX), and $rate$ is what I called speed in my original answer, renamed for clarity.

The $rate$ variable determines how close to the target we get each second. A value of 0.5 means every second we are half as close to the target as the previous second. A value of 1 means we are "instantly teleported" to our target in that second, and a value of 0 means we never reach the target.

And for some actual code, the following should be valid in C#, and fully framerate independent (even supports reversing time, though, you might get some funny results if you go into negative time):

x = startX + (targetX - startX) * (1 - Math.Pow(1-rate, currentTime - startTime));