Search Unity

How is angular drag applied to a Rigidbody in Unity? (How is angular damping applied in PhysX?)

Discussion in 'Physics' started by ShadyGuy, Nov 23, 2015.

  1. ShadyGuy

    ShadyGuy

    Joined:
    Oct 18, 2015
    Posts:
    13
    I'm predicting where a GameObject's transform.forward will be in X number of FixedUpdate() calls.

    I got it 100% accurate when the Rigidbody's angular drag is 0 but if it's for example the default 0.05 I get somewhat close accuracy but not 100% correct. The error also gets larger the further into the future I'm trying to predict where the transform.forward will be.

    Here is how I reduce the angular velocity of the Rigidbody right now:

    Code (csharp):
    1. Vector3 rigidbodyAngularVelocity = GetComponent<Rigidbody>().angularVelocity;
    2. float rigidbodyAngularDrag = GetComponent<Rigidbody>().angularDrag;
    3.  
    4. ...
    5.  
    6. float dragForceMagnitude = Mathf.Pow(rigidbodyAngularVelocity.magnitude, 2) * rigidbodyAngularDrag;
    7. Vector3 dragForceVector = dragForceMagnitude * rigidbodyAngularVelocity.normalized * -1;
    8. rigidbodyAngularVelocity += dragForceVector * Time.fixedDeltaTime;
    However it seems like this isn't exactly how PhysX is applying the angular drag (or angular damping as they call it).

    So my question is what is their calculation formula?
    (In programmer terms, please.)

    I know that angular drag is the problem because if I set it to 0 I get correct predictions no matter how far into the future I try to predict. I'm also aware of the workaround of setting angular drag to 0 on the Rigidbody and then applying angular drag myself in the FixedUpdate() but I don't want to have to modify the Physics only to predict them...

    I'm willing to accept a non-accurate prediction just as long as I know I'm using the same calculations that PhysX is using (errors due to float rounding limitations etc is fine). I've searched around a lot and can't find them... Just the code above (from non-official sources) and it doesn't give accurate results so I'm guessing it's just someone's guess on a forum.
     
    Last edited: Nov 23, 2015
  2. ShadyGuy

    ShadyGuy

    Joined:
    Oct 18, 2015
    Posts:
    13
    After many tests I've determined that this highly advanced formula reveals the secrets to PhysX's angular dampening:

    Code (csharp):
    1. Vector3 rigidbodyAngularVelocity = GetComponent<Rigidbody>().angularVelocity * Time.fixedDeltaTime;
    2. float rigidbodyAngularDrag = GetComponent<Rigidbody>().angularDrag * Time.fixedDeltaTime;
    3.  
    4. ...
    5.  
    6. if (rigidbodyAngularDrag>1) {
    7.    rigidbodyAngularVelocity = Vector3.zero;
    8. } else if (rigidbodyAngularDrag>0) {
    9.    rigidbodyAngularVelocity -= rigidbodyAngularDrag * rigidbodyAngularVelocity;
    10. }
    Simple as that. Notice that if your Rigidbody has a angular drag higher than or equal to the number of FixedUpdate() calls per second in your game the Rigidbody will always stop spinning immediately (rigidbodyAngularDrag becomes 1 or greater).

    Now, I'm not completely sure if this really is PhysX/Unity's way of applying angular dampening but it's what I get closest results with even when predicting 30 seconds into the future. The prediction is not 100% but it's close enough for me to believe that float limitations or other stuff that can't really be controlled is the cause of the mismatch. It also makes sense for the physics engine to avoid expensive Mathf.Pow() calls so yeah, this is probably a wrap folks.

    Please post if you have anything else to add, especially corrections. I've already noticed that this thread comes up when searching the net for "how does unity apply angular drag to gameobjects" or similar so it'd be nice to put as much info on the subject as possible here.
     
  3. any_user

    any_user

    Joined:
    Oct 19, 2008
    Posts:
    374
    I don't know what physx is doing internally, but normally I do this for drag-like behaviour in my own code:

    Code (CSharp):
    1. velocity *= Mathf.Exp(-drag * Time.deltaTime);
    Time.deltaTime is anyway fixed in this case, but this works better than your approach for varying frame rates (but there's still *some* precision error compared to analytic solving).
     
  4. YoyoMario

    YoyoMario

    Joined:
    Apr 8, 2013
    Posts:
    16
    Time.deltaTime is never "fixed" even if you restrict fps to any amount, it's never fixed...
     
    Last edited: Apr 29, 2022
  5. any_user

    any_user

    Joined:
    Oct 19, 2008
    Posts:
    374
    This is assuming you use the code in FixedUpdate, where Time.deltaTime is equals Time.fixedDeltaTime. The "exp" formula results stay more predictable when changing the physics time step (eg. during development). It's not meant to work for *real* varying frame rates like in Update().

    This would be the same code, with unambiguous deltaTime and more context:
    Code (CSharp):
    1. void FixedUpdate()
    2. {
    3.     velocity *= Mathf.Exp(-drag * Time.fixedDeltaTime);
    4. }
     
    ivaylo5ev likes this.
  6. YoyoMario

    YoyoMario

    Joined:
    Apr 8, 2013
    Posts:
    16
    I was just saying that Time.deltaTime is never equal to Time.fixedDeltaTime, not even if you use it in FixedUpdate...
    Maybe the language barrier is messing what you're trying to say.
    From Unity documentation, FixedUpdate is executed approximately 1/fixedTimeStep amount of times per seconds, that means, your fixed update can be called twice before another update loop is called, but fixed update will execute the amount of times needed per second, but isn't fixed to work in correlation with update loop.
     
    Last edited: Apr 29, 2022
  7. JesterGameCraft

    JesterGameCraft

    Joined:
    Feb 26, 2013
    Posts:
    452
    Assuming that X will remain constant. Could you not just do an example run and get a value of where the fwd is at that X time. Then create an empty game object, make it a child of your parent and point it at the final X orientation of where fwd was pointing. Then when you need to predict where fwd will be just query where the empty game object is pointing. Not sure if that would satisfy your requirement.
     
  8. SparrowGS

    SparrowGS

    Joined:
    Apr 6, 2017
    Posts:
    2,536
    That not true afaik, it doesn't care where its called, it will return the time from the last visual frame.
     
  9. any_user

    any_user

    Joined:
    Oct 19, 2008
    Posts:
    374
    I always assumed that's the case, based on what's written in the documentation. Maybe I misread the docs?
    There it says:
    https://docs.unity3d.com/ScriptReference/Time-fixedDeltaTime.html
     
    AnomalusUndrdog and SparrowGS like this.
  10. SparrowGS

    SparrowGS

    Joined:
    Apr 6, 2017
    Posts:
    2,536
    I stand corrected.

    How does it know where its called from?
     
    TorbenDK likes this.
  11. nucleartide

    nucleartide

    Joined:
    Jun 15, 2012
    Posts:
    16
    ShadyGuy's post is correct, but angular drag values above 1 are also fine. From PhysX's documentation on damping:

    So the logic would be:

    Code (csharp):
    1. rigidbody.angularVelocity *= Mathf.Clamp01(1f - angularDrag * Time.fixedDeltaTime);
    This means that angularDrag can be a value above 1 (say 5). For a `Time.fixedDeltaTime` of .02, this gives:

    Code (csharp):
    1. rigidbody.angularVelocity *= Mathf.Clamp01(1f - 5f * .02);
    Which is the same as running the following on every physics timestep:

    Code (csharp):
    1. rigidbody.angularVelocity *= .9f;