Skip to main content
Better justification
Source Link
DMGregory
  • 141k
  • 23
  • 258
  • 401

Real air resistance is a force proportional to cross sectional area and speed (or proportional to the square of speed at high speeds), and heavy objects accelerate less for the same force, so that's where the intuitive idea that the heavy object should fall faster comes from. But this is much more expensive to calculate, even approximately, soand different games will have different needs in terms of speed/realism trade-offs, or different types of fluid interactions they need to model. So for efficiency, physics engines don't do thatmake any attempt to model this messy phenomenon by default — they just give you a cheap way to make things slow down and stop jittering.

Real air resistance is a force proportional to cross sectional area and speed (or proportional to the square of speed at high speeds), and heavy objects accelerate less for the same force, so that's where the intuitive idea that the heavy object should fall faster comes from. But this is much more expensive to calculate, even approximately, so for efficiency, physics engines don't do that by default.

Real air resistance is a force proportional to cross sectional area and speed (or proportional to the square of speed at high speeds), and heavy objects accelerate less for the same force, so that's where the intuitive idea that the heavy object should fall faster comes from. But this is much more expensive to calculate, even approximately, and different games will have different needs in terms of speed/realism trade-offs, or different types of fluid interactions they need to model. So for efficiency, physics engines don't make any attempt to model this messy phenomenon by default — they just give you a cheap way to make things slow down and stop jittering.

Adding limit to make sure drag doesn't add jitters
Source Link
DMGregory
  • 141k
  • 23
  • 258
  • 401
[RequireComponent(typeof(Rigidbody))]
public class AirDrag : MonoBehaviour {

    public float dragCoefficient;

    // You could calculate this automatically from the colliders / bounds,
    // or even recompute it for each direction of travel.
    // Tumbling bodies are left as an exercise for the reader. 😜
    public float crossSectionalArea;

    [SerializeField, HideInInspector]
    Rigidbody _body;

    void OnValidate() {
        if (_body == null) TryGetComponent(out _body);
    }

    void FixedUpdate() {
        Vector3 vel = _body.velocity;
        float speed = vel.magnitude;

        // Assume high speed flow, drag proportional to square of speed.
        Vector3 drag = -vel * vel.magnitude;speed * dragCoefficient * crossSectionalArea;

        // How much force would be required to stop the object entirely?
        float limit = _body.AddForcemass * speed / Time.deltaTime;

        // Ensure we never accelerate more than that (no reversing direction).
        drag *= dragCoefficientVector3.ClampMagnitude(drag, *limit);

 crossSectionalArea       // Apply the clamped drag.
        _body.AddForce(drag);
    }
}
[RequireComponent(typeof(Rigidbody))]
public class AirDrag : MonoBehaviour {

    public float dragCoefficient;

    // You could calculate this automatically from the colliders / bounds,
    // or even recompute it for each direction of travel.
    // Tumbling bodies are left as an exercise for the reader. 😜
    public float crossSectionalArea;

    [SerializeField, HideInInspector]
    Rigidbody _body;

    void OnValidate() {
        if (_body == null) TryGetComponent(out _body);
    }

    void FixedUpdate() {
        Vector3 vel = _body.velocity;

        // Assume high speed flow, drag proportional to square of speed.
        Vector3 drag = -vel * vel.magnitude;

        _body.AddForce(drag * dragCoefficient * crossSectionalArea);
    }
}
[RequireComponent(typeof(Rigidbody))]
public class AirDrag : MonoBehaviour {

    public float dragCoefficient;

    // You could calculate this automatically from the colliders / bounds,
    // or even recompute it for each direction of travel.
    // Tumbling bodies are left as an exercise for the reader. 😜
    public float crossSectionalArea;

    [SerializeField, HideInInspector]
    Rigidbody _body;

    void OnValidate() {
        if (_body == null) TryGetComponent(out _body);
    }

    void FixedUpdate() {
        Vector3 vel = _body.velocity;
        float speed = vel.magnitude;

        // Assume high speed flow, drag proportional to square of speed.
        Vector3 drag = -vel * speed * dragCoefficient * crossSectionalArea;

        // How much force would be required to stop the object entirely?
        float limit = _body.mass * speed / Time.deltaTime;

        // Ensure we never accelerate more than that (no reversing direction).
        drag = Vector3.ClampMagnitude(drag, limit);

        // Apply the clamped drag.
        _body.AddForce(drag);
    }
}
Source Link
DMGregory
  • 141k
  • 23
  • 258
  • 401

Check out the implementation of linearDamping in PhysX here (what Unity calls Linear Drag in the Rigidbody built on top of PhysX). It's equivalent to:

velocity = velocity * (1f - linearDamping * Time.deltaTime);

This is not a physically accurate model of air resistance - it's just a cheap calculation to let excess velocity decay away, damping down jitters in the physics system.

You'll notice that it doesn't include mass. Neither does acceleration due to gravity (a heavy object feels a greater force of gravitational attraction, but also requires more force to accelerate, and these two effects exactly cancel each other out).

So that means in your example, the only difference between these three objects is that one has a damping of 0.3 and the others have a damping of 0.1.

As I show in this spreadsheet sim, that's only enough to delay the impact of the first object by 0.04 seconds over a 5 meter drop - that's only a little more than a single frame at 30 fps - so it's not surprising that there's no perceptible difference here.

Spreadsheet simulation

Real air resistance is a force proportional to cross sectional area and speed (or proportional to the square of speed at high speeds), and heavy objects accelerate less for the same force, so that's where the intuitive idea that the heavy object should fall faster comes from. But this is much more expensive to calculate, even approximately, so for efficiency, physics engines don't do that by default.

But that doesn't stop you from writing your own air resistance calculation - to whatever degree of accuracy is appropriate for your game's needs - and injecting that into the physics tick using FixedUpdate, something like this:

[RequireComponent(typeof(Rigidbody))]
public class AirDrag : MonoBehaviour {

    public float dragCoefficient;

    // You could calculate this automatically from the colliders / bounds,
    // or even recompute it for each direction of travel.
    // Tumbling bodies are left as an exercise for the reader. 😜
    public float crossSectionalArea;

    [SerializeField, HideInInspector]
    Rigidbody _body;

    void OnValidate() {
        if (_body == null) TryGetComponent(out _body);
    }

    void FixedUpdate() {
        Vector3 vel = _body.velocity;

        // Assume high speed flow, drag proportional to square of speed.
        Vector3 drag = -vel * vel.magnitude;

        _body.AddForce(drag * dragCoefficient * crossSectionalArea);
    }
}