Calculating the force of drag on a rigidbody to overcome it for movement?

Hello! First time poster, and having some issues with my Rigidbody based player controller. I’m trying to avoid any shortcuts here as much as possible, and building something that physically accurate.

public virtual void Move()
{
    Vector2 Input = biped.movementInput.ReadValue<Vector2>();

    //Don't hit the breaks on an empty input. Drag does that.
    if (Input == Vector2.zero) return;

    Vector3 DesiredVelocity = new Vector3(Input.x, 0, Input.y) * GetMovementSpeed.Invoke();

    Vector3 planarVelocity = biped.transform.InverseTransformDirection(biped.rb.velocity);
    planarVelocity.y = 0;

    float dragScalar = 1.0f - biped.rb.drag * Time.fixedDeltaTime;
    if (dragScalar < 0.0f) dragScalar = 0.0f;
    Vector3 DesiredNoDragVelocity = DesiredVelocity / dragScalar;

    Vector3 RequiredVelocityChange = DesiredNoDragVelocity - planarVelocity;

    //Make sure the force and the input direction match.
    //Exit case - Input 0,1 -> 0,0.3 = Trying to go low speed, let drag handle it.
    //Exit case - Speed 999 = The velocity change would be mega neg to try to slow us down. Don't.
    //Stay case - Input 0,1 -> 0,-1 = Switching direction.
    if (Vector2.Dot(new Vector2(RequiredVelocityChange.x, RequiredVelocityChange.z).normalized, Input.normalized) < 0) { return; }

    Vector3 MovementForce = biped.rb.mass * RequiredVelocityChange / Time.fixedDeltaTime;

    float AccelerationLimit = GetAccelerationLimit.Invoke();

    if(MovementForce.magnitude > AccelerationLimit)
    {
        MovementForce = MovementForce.normalized * AccelerationLimit;
    }

    biped.rb.AddRelativeForce(MovementForce, ForceMode.Force);
}

This is my movement function.

float dragScalar = 1.0f - biped.rb.drag * Time.fixedDeltaTime;
if (dragScalar < 0.0f) dragScalar = 0.0f;
Vector3 DesiredNoDragVelocity = DesiredVelocity / dragScalar;

This snippet of code is based on this forum thread, and when tested it seemed to be correct, as long as drag is applied before the movement of the object in a given tick.

Undoing the drag in this way gets our player up to the correct speed, where before they would be limited to the target speed after one tick of drag is applied (Target speed of 2 with a drag value of 5 would cap us at 1.8 when our fixed update ticks at 50hz).

Vector3 MovementForce = biped.rb.mass * RequiredVelocityChange / Time.fixedDeltaTime;

The movement force calculated here would get us up to our target velocity in a single fixed update. We limit this value to (the poorly named) acceleration limit, which is calculated as mass * (v / t) where mass is the rigidbody’s mass, v is the target speed, and t is the time we want it to take for the player to accelerate up to their movement speed from rest. With no drag value, this works perfectly. However, we also need to increase this limit to compensate for the drag slowing us down. Since drag is dependent on our velocity, we would need to calculate the drag force’s magnitude during the fixed update and add it to the acceleration limit.

What I’m struggling with is how to calculate that drag force. Obviously F=ma, and a = delta_v/delta_t, and so it’d be something like

Vector3 VelocityWithDrag = biped.rb.velocity;
Vector3 VelocityWithoutDrag = VelocityWithDrag / dragScalar;

var dragForce = biped.rb.mass * (VelocityWithoutDrag - VelocityWithDrag) / Time.fixedDeltaTime;

float AccelerationLimit = GetAccelerationLimit.Invoke() + dragForce.magnitude;

but this isn’t working as we accelerate a little too slowly. Clearly, I need a different velocity value in there, but my brain is fried trying to figure this out, and I think I need a second set of eyes. Any help is greatly appreciated.

That’s not really a thing. A biped walks by actually falling continuously and then lifting themselves with the next foot being in the right place.

Not only that but computer physics are simulated discretely one frame at a time. The real world is actually continuous. Plan accordingly.

Aerodynamic drag can be modeled as a damping fraction percentage per time. This is cute but doesn’t really feel right for walking: you drift forever and ever, slower and slower until you stop.

Therefore usually a bipedal controller has both a linear and geometric term to the velocity damping to get you to zero in a reliable fashion.

And at the end of the day you’re trying to make something fun, so often times inhibiting damping when the player is actively controlling the speed is done.

I agree with Kurt. I wouldn’t get too caught up on recreating realistic drag because it isn’t really drag that brings a person to a stop but rather it’s the person leaning backwards in the opposite direction and pushing their feet into the ground.

If not using the rigidbody’s own drag setting to bring a character to a stop then I simply do this:

rb.AddForce(input * 20 - rb.velocity * 4); 

Right, I guess worded that poorly. My point was more so that I’m trying to build a character controller that is built with physics, and I’m trying to avoid shortcuts whenever possible so that I don’t have to deal with the potential issues that might pop up as a result. I know on smaller projects in the past I’ve done plenty of that, while here I’m trying to build something quite a bit nicer that I can reuse.

Generally, drag worked well with an earlier implementation of movement where I simply capped my velocity, but I had to move away from that method since it wasn’t accurately interacting with physics objects as I walked into them. I wanted my character to be slowed down as they push against them. That’s how I got to this new implementation, but drag was causing me issues.

I guess just don’t rely on drag for stopping then, and instead have something separate for bringing my player to a stop.

Perhaps you’re looking for something like this?

If it helps, this is the exact formula that replicates the Rigidbody drag:

rigidbody.velocity *= Mathf.Clamp01(1f - rigidbody.drag * Time.fixedDeltaTime);

Source