Calculate future position of an arcade physics body

For any maths people out there, I need a method for predicting the future position of body, given an amount of time in the future as an argument. I am using arcade physics and the body has drag applied and useDamping set to true, so the drag effect needs to be taken into consideration.

I know there is a maths equation for estimating this (ut+1/2at^2), but I cannot get that to work with arcade physics.

Instead I have a work around method where I am iterating through frames to work out the position through accumulation. See below. But this is inefficient and unsatisfactory and I expect this can be achieved without any loops at all by using a simple equation.

If anyone with a maths brain has a better solution it would be greatly appreciated.

Cheers.

public futurePosition(time: number): Phaser.Math.Vector2 {
  const DELTA = 1 / 60; // Assume we are running at 60fps.
  const position = this.position.clone(); // Current position of body
  const velocity = this.body.velocity.clone(); // Current velocity

  // Inefficiently looping through frames
  for (let i = 0; i < time / (DELTA * 1000); i++) {
    velocity.x *= Math.pow(BALL_DRAG, DELTA);
    velocity.y *= Math.pow(BALL_DRAG, DELTA);
    position.x += velocity.x * DELTA;
    position.y += velocity.y * DELTA;
  }

  return position;
}

If don’t need to run this function often and don’t want to calculate a position too far into the future, this solution is the most accurate one. Actually, it’s what Phaser would do it had to catch up to real time. To reduce the iterations, you can decrease the FPS at the cost of accuracy.

Too remove the loop entirely, you can simulate a variable time step by using time * 0.001 (or passing seconds into the function and using time directly) instead of DELTA. This is what Phaser would do to catch up if fixedStep is false. It’s decently accurate if the velocity remains constant, but quickly breaks down if you have to account for drag.

I’m not sure if there’s an accurate way to simulate this that doesn’t require looping. As far as I’m aware, most physics simulations tend to misbehave if you extrapolate too much. Maybe someone else could give better advice.

Thanks Telinc1. I do not need to run this function often and it should never need to predict further than a couple of seconds into the future. So the looping solution works fine. But it still bothers me. There must be a good equation for estimating this, surely :thinking:.

Your formula is for constant acceleration.
This looks more like a geometric progression.

Try (v0 * (1 - Math.pow(r, n))) / (1 - r);
where
v0 is the starting velocity
r is the ratio (e.g. 0.99, if v1 = v0 * 0.99
n is the number of terms (frames)

1 Like

Thanks Milton. I tried the below but the newX the formula is predicting is too low a value. I must be doing something wrong or maybe there is an extra time multiplication required on top :thinking: .

const delta = 1 / 60;
const frames = timeInSeconds * DELTA;
const position = this.position.clone();
const velocity = this.body.velocity.clone();
const r = Math.pow(0.3, delta);
const vx = (velocity.x * (1 - Math.pow(r, frames))) / (1 - r);
const newX = position.x + vx;

Hmm, velocity is per second right?
That should be per frame i think…
Which would give you an even lower value :smile:

If I try with v0 = 2, r = 0.99, n from 1 to 10, I get:
2
3.980000000000002
5.94019999999999
7.880798000000004
9.801990020000009
11.703970119800012
13.586930418602005
15.451061114415973
17.29655050327182
19.1235849982391

Seems correct to me.

Yeah velocity is per second. I had to divide that by fps. I now have this which works great. Thanks Milton.

public futurePosition(timeInSeconds: number): Phaser.Math.Vector2 {
  const fps = 60;
  const drag = Math.pow(0.3, 1 / fps);
  const x = ((this.body.velocity.x / fps) * (1 - Math.pow(drag, timeInSeconds * fps))) / (1 - drag);
  const y = ((this.body.velocity.y / fps) * (1 - Math.pow(drag, timeInSeconds * fps))) / (1 - drag);

  return new Phaser.Math.Vector2(this.position.x + x,  this.position.y + y);
}