Unity: Make a moving rigidbody stop exactly at a given distance

I have an object that only moves along Z-axis, its rigidbody has a mass of 1 and a drag of 0.2.

That object accelerates till MaxVelocity and then moves at that constant velocity in the Z-axis.

When the user pushes the brakes I want to add a force on that rigidbody to make it stop completely at a given distance (lets say 10 meters from where it pushed the button).

I've tried using: .

Which is what you get when solve for t in the first and replace in the second:

Where v is final velocity and in my case I want it to be zero.

The code

I call this method every FixedUpdate since user pushed brakes:

private float CalculateBrakeAccel(float targetPos)
{
// targetPos has the position in the z axis the object should stop
// i.e.: velocity is 0.0f
float remainingDistance = (targetPos - rigidbody.position.z);
// formula: a = 0.5 * v0^2 / (e0 - e)
float brake = 0.5f * rigidbody.velocity.sqrMagnitude / remainingDistance;
return brake;
}


That alone always results in a braking distance less than the expected (passing 10m as Braking distance and the object brakes at ~8.4m). I thought it could be drag. So I tried to compensate my braking accel with the drag (for the drag calculation I used the formula found here):

public float Accel = 8f;
private float maxVelocity = 19.44f; // in m/s, approx. 70km/h
[...]
void FixedUpdate ()
{
Vector3 forwardForce;
if (playerController.PowerInput < 0.0f)  // if user braking, PowerInput = -1
{
float brakeAccel = CalculateBrakeAccel(playerController.BrakeTarget); // BrakeTarget is the transform.position.z where the user pushed the brakes + 10 meters
forwardForce = new Vector3(0.0f, 0.0f, brakeAccel * playerController.PowerInput);
Vector3 drag = rigidbody.velocity * Mathf.Clamp01(1f - rigidbody.drag * Time.fixedDeltaTime);
if (drag.z > 0.0f)
forwardForce += drag;
else
{
forwardForce -= drag;
}
}
if (!CapAtMaxSpeed())    // if max. vel not reached, accel force can be applied:
{
forwardForce = new Vector3(0.0f, 0.0f, playerController.PowerInput * Accel);
}
}

bool CapAtMaxSpeed()
{
// velocityMagnitude == rigidbody.velocity.magnitude but without sqrt() operation.
velocityMagnitude = Vector3.Dot(rigidbody.velocity, myTransform.forward);
if (velocityMagnitude > maxVelocity)
{
rigidbody.velocity = rigidbody.velocity.normalized * maxVelocity;
return true;
}
return false;
}


That code gave the best results till now for any given distance (but with that specific Accel and maxVelocity). For example, it returns something in between 9.93-9.97 meters for a given distance of 10 meters (which I would accept as valid enough and a precision issue with floating operations).

The problem is if I encrease the Accel or the maxVelocity, the distance the object reaches full stop is progressively less and less than the one I'm passing to the script.

Also, if I push brake while not at maxVelocity and still accelerating (i.e., applying positive force along Z-axis), the braking distance is way shorter. Common sense suggests it should be larger in any case. And CalculateBrakeAccel() is using the current velocity at any given moment so the correct accel should be a calculated.

• I cannot go try on Unity now, but I was wondering, why instead of creating a force don't you use the rigidbody drag directly? You said that it is set to 0.2 now, you could try and make it so that if you want to brake you use rb.drag = x , and then you just have to tweak the value of x a little bit. – FSic Aug 20 '19 at 13:08
• You don't have full control over the time component. Let's say unity physx runs at 100fps. Then you have a precision of 10ms, thus you have a low probability of the object being at the desired position in one of the frames. The faster the object, the more it moves in between frames.. What you can do is detect that the object is to far, and then move it to the desired position. – Chillersanim Aug 20 '19 at 13:37