Log in
Please log in or register.
Pages: [1]
  Print  
Author Topic: Exact time-based distance calculation: solved  (Read 1354 times)
Reptangle
Newbie
*
Posts: 9


View Profile
« on: Sat, Nov 28, 2009 »

Hi!

I have something handy to share. I was a bit annoyed by the fact that the way Flixel currently calculates it, the distance travelled  in a given time by a sprite with acceleration doesn't match exactly for any time delta between frames. For example, if I have a character jumping and the velocity is only set at the beginning of the jump, the max height reached by that character will be lower if the general framerate is 30 fps than if it's something higher, let's say 60.

**Before going further, I just want to have it told that the way flixel already handles things is certainly sufficient for most applications. No huge flaw here... just a way to get more accuracy should you feel like you need it. And so far it seems like a very stable method.

Ok so, here's what happens:
Each update cycle, velocity is first calculated like this: velocity + (acceleration * time delta)
Then the new position is obtained: position + velocity * time delta.

The velocity is calculated correctly. Hovewer the new position isn't, since the position has been incremented during "time delta" time, by "velocity", which itself was constantly changing during that same time to reach the new velocity... so we can't simply multiply the new velocity by the time delta and add this to the position.

To confirm this, I analyzed what happens at two different framerates:
(the positions that are supposed to be matching according to time are in bold)

-at 30 fps, a sprite accelerates by 2 every frame, starting at velocity 0.
-the delta time is twice the one of 60 fps so we increment the position by 2* the velocity.
distance travelled in 8 steps:
0 --> +2*(0+2)=4 --> +2*(2+2)=12 --> +2*(4+2)=24 --> +2*(6+2)=40 --> + 2*(8+2)=60 --> +2*(10+2)=84 --> +2*(12+2)=112 --> +2*(14+2)=144

-at 60 fps, that sprites logically accelerates by 1 every frame
distance travelled in 16 steps (which is expected to be 144)
0 --> +(0+1)=1 --> +(1+1)=3 --> +(2+1)=6 --> +(3+1)=10 --> +(4+1)=15 --> +(5+1)=21 --> +(6+1)=28 --> +(7+1)=36 --> +(8+1)=45 --> +(9+1)=55 --> +(10+1)=66 --> +(11+1)=78 --> +(12+1)=91 --> +(13+1)=105 -->+(14+1)=120 -->+(15+1)=136

You can see that the position obtained with the 30 fps simulation is gradually distancing the one obtained with the 60 fps simulation. That explains what I said earlier about a lower jump at a lower framerate, since the character is brought back to the ground faster.

To fix this, I searched for a physics forumula to get the distance travelled by an object in function of time and acceleration (Since I don't remember that from high school D: )

I found this:
s = ut + 1/2 at2
in words:
distance travelled = initial velocity*time delta + (acceleration*(time delta2))/2

With that knowledge, I edited FlxSprite.update() so that it still calculates the velocity with FlxG.computeVelocity() buuut right before that, the x and y distances travelled are calculated using the above distance calculation function, which I added to FlxG. The result is this:

in FlxSprite.update()
Code:
// distance calculation
var distX:Number = FlxG.computeDistance(velocity.x, acceleration.x + thrustComponents.x, drag.x);
var distY:Number = FlxG.computeDistance(velocity.y, acceleration.y + thrustComponents.y, drag.y);

// max velocity checking
var maxV:Number = maxVelocity.x * FlxG.elapsed;
if (distX > maxV) {
distX = maxV;
} else if (distX < -maxV) {
distX = -maxV;
}
maxV = maxVelocity.y * FlxG.elapsed;
if (distY > maxV) {
distY = maxV;
} else if (distX < -maxV) {
distY = -maxV;
}

x += distX;
y += distY;

// velocity calculation
velocity.x = FlxG.computeVelocity(velocity.x,acceleration.x+thrustComponents.x,drag.x,maxVelocity.x);
velocity.y = FlxG.computeVelocity(velocity.y, acceleration.y + thrustComponents.y, drag.y, maxVelocity.y);
instead of:
Code:
x += (velocity.x = FlxG.computeVelocity(velocity.x,acceleration.x+thrustComponents.x,drag.x,maxVelocity.x))*FlxG.elapsed;
y += (velocity.y = FlxG.computeVelocity(velocity.y,acceleration.y+thrustComponents.y,drag.y,maxVelocity.y))*FlxG.elapsed;

And here's the FlxG.computeDistance() function:
Code:
static public function computeDistance(Velocity:Number, Acceleration:Number, Drag:Number = 0):Number
{
var acc:Number = 0;
if (Acceleration != 0)
acc = Acceleration;
else if(Drag != 0)
{
if(Velocity - Drag > 0)
acc = -Drag;
else if(Velocity + Drag < 0)
acc = Drag;
else
acc = -Velocity;
}
return Velocity * FlxG.elapsed + (acc * FlxG.elapsed * FlxG.elapsed) / 2;
}

And that's it!
So far I didn't notice any anomaly since I switched to this method, and now my character jumps at the same height no matter what the framerate is. I tested with up to 50 sprites and the difference in speed was not noticeable  Grin

I think this would be a nice addition to Flixel, as an option perharps. A FlxSprite property like "accurateDistCalc" could toggle between the two methods...
« Last Edit: Sat, Nov 28, 2009 by Reptangle » Logged
Pages: [1]
  Print  
 
Jump to: