I am writing a script in unity that applies a force to the object to advance it a set distance in an environment without friction or drag, producing a cosine force curve.
However, as the target approaches the destination, the force to apply is less than the value specified by the curve as the time unit is too large and will cause the object to reverse direction.
To resolve this, I estimate what the speed will be after applying this frames force and, if that reverses the direction, we take a proportion of the force.
It is working well around 4 times out of 5, but every so often it will not detect the reverse in direction and will apply the force for an extra frame or 2, causing the object to back up.
Closer investigation revealed that basically the problem stems from my estimate being significantly (some times up to 0.05) different from the velocity the next frame. I compute it as follows:
Vector3 currentVelocity = ship.rigidbody.velocity;
Vector3 direction = ship.transform.right;
// this is the value I use for the "actual" speed
float velocityAlongDir = Vector3.Dot(currentVelocity, direction);
// this is my estimate
float estimatedVelocity = velocityAlongDir + (applyingForce * lDeltaTime);
Where the applyingForce
is applied thus:
ship.rigidbody.AddForce((applyingForce * ship.rigidbody.mass) * transform.right);
Some sample data:
Predicted velocity: 0.3048308
Actual velocity: 0.2880591
Predicted velocity: 0.2068605
Actual velocity: 0.1900333
Predicted velocity: 0.09798826
Actual velocity: 0.1900333
Predicted velocity: 0.1082982
Actual velocity: -0.007007703
As you can see by the last entry, the predicted value doesn't change sing, but the real value does. Trying to work around this using does it go below a small value get different problems where the current velocity is too small etc.
Any idea why my calculation for next frame velocity seems so wrong and is there anything I can do about it?
Update
As pointed out in the comments, I needed to apply the force in the FixedUpdate
and calculate my next frames velocity using Time.fixedDeltaTime
. However, the following can happen, presumably in adverse frame conditions:
Predicted Velocity: 15.67547
Actual Velocity: 15.67547
Predicted Velocity: 17.54726
Actual Velocity: 15.67547 // note the same as the previous update, there has been no physics step!
Predicted Velocity: 17.51759 // estimate is subtly different, because my own timer has advanced
I fixed this by using fixedTime
as opposed to fixedDeltaTime
. However, then when I do a double update I am wrong for that frame, its like the physics step is trying to make up for the lost time:
// all going well...
Predicted Velocity: 52.25977
Actual Velocity: 52.25977
Predicted Velocity: 52.40138
// the estimate is wrong, we are moving at the same speed => no physics update
Actual Velocity: 52.25977
// we predict the same velocity for next frame
Predicted Velocity: 52.40138
// but we are wrong, like it is trying to catch up for lost time
Actual Velocity: 52.543
// then after that point we are precisely right
Predicted Velocity: 52.66479
Actual Velocity: 52.66479
This wouldn't be an issue except in the edge case where we do this double step precisely at the point we need to detect when the speed will change and apply the ratio. Since the physics step seems to play catch up, I miss the fact we changed speed.
update
method, whereas physics ought to be computed in thefixedUpdate
method. \$\endgroup\$AddForce
call in to theFixedUpdate
but was sufficient to make the estimated velocity calculation depend onTime.fixedDeltaTime
. In fact, leaving the calculation in the Update made the "estimate" accurate to all the decimal places printed (whereas in Fixed, only the first 3 matched) and stops the ship dead, rather than moving at a small velocity. \$\endgroup\$FixedUpdate()
, it sounds like some is still running inUpdate()
. \$\endgroup\$