Search Unity

How to calculate how much torque will rigidbody.addForceAtPosition add?

Discussion in 'Physics' started by jtomes123, Dec 23, 2014.

  1. jtomes123

    jtomes123

    Joined:
    Dec 18, 2012
    Posts:
    7
    Hi, I decided to make "realistic" space game. I have working thrusters and now I am trying to create something similar to flight assistant which would help you to turn your ship, but I don't know how to calculate torque that will be added when I perform addForceAtPosition.
     
  2. MatthewW

    MatthewW

    Joined:
    Nov 30, 2006
    Posts:
    1,356
    I'm pretty sure the Unity function just maps directly to PhysX's addForceAtPos() function, which doesn't expose or return any of the math.

    I guess you could look into building your own AddForceAtPosition function, so you know exactly what's going on under the hood, or try to build the flight assistant around watching other parameters like rigidbody.angularVelocity after the fact...

    http://docs.unity3d.com/ScriptReference/Rigidbody-angularVelocity.html
     
  3. Partel-Lang

    Partel-Lang

    Joined:
    Jan 2, 2013
    Posts:
    2,554
    Hi!

    Code (CSharp):
    1. /// <summary>
    2.     /// Calculates the change of angular velocity for the attached Rigidbody if a force were to applied to it at a position.
    3.     /// </summary>
    4.     /// <returns>Angular velocity delta.</returns>
    5.     /// <param name="force ">Force (world space).</param>
    6.     /// <param name="position)">Position (world space).</param>
    7.     /// <param name="forceMode">Force mode.</param>
    8.     public Vector3 ForceToTorque(Vector3 force, Vector3 position, ForceMode forceMode = ForceMode.Force) {
    9.         Vector3 t = Vector3.Cross(position - rigidbody.worldCenterOfMass, force);
    10.         ToDeltaTorque(ref t, forceMode);
    11.  
    12.         return t;
    13.     }
    14.    
    15.     private void ToDeltaTorque(ref Vector3 torque, ForceMode forceMode) {
    16.         bool continuous = forceMode == ForceMode.Force || forceMode == ForceMode.Acceleration;
    17.         bool useMass = forceMode == ForceMode.Force || forceMode == ForceMode.Impulse;
    18.        
    19.         if (continuous) torque *= Time.fixedDeltaTime;
    20.         if (useMass) ApplyInertiaTensor(ref torque);
    21.     }
    22.    
    23.     private void ApplyInertiaTensor(ref Vector3 v) {
    24.         v = rigidbody.rotation * Div(Quaternion.Inverse(rigidbody.rotation) * v, rigidbody.inertiaTensor);
    25.     }
    26.    
    27.     private static Vector3 Div(Vector3 v, Vector3 v2) {
    28.         return new Vector3(v.x / v2.x, v.y / v2.y, v.z / v2.z);
    29.     }
    The first function will give you the delta angular velocity that would be added to the Rigidbody if you called AddForceAtPosition with the exact same parameters.

    Linear velocity (rigidbody.velocity) delta just equals to force, but you'll have add fixedDeltaTime and divide by mass similar to whats happening in ToDeltaTorque to account for the different force modes.

    Cheers,
    Pärtel
     
  4. BenZed

    BenZed

    Joined:
    May 29, 2014
    Posts:
    524
    Hey @Partel Lang! I just found this answer after searching for a similar answer for 2D physics.

    This math is way beyond me! How would I adapt these functions to work in 2D?

    Thanks in advance :)
     
  5. Partel-Lang

    Partel-Lang

    Joined:
    Jan 2, 2013
    Posts:
    2,554
    Hey, no sweat :)

    Code (CSharp):
    1. public float ForceToTorque(Vector2 force, Vector2 position, ForceMode2D forceMode = ForceMode2D.Force) {
    2.         // Vector from the force position to the CoM
    3.         Vector2 p = rigidbody2D.worldCenterOfMass - position;
    4.  
    5.         // Get the angle between the force and the vector from position to CoM
    6.         float angle = Mathf.Atan2(p.y, p.x) - Mathf.Atan2(force.y, force.x);
    7.  
    8.         // This is basically like Vector3.Cross, but in 2D, hence giving just a scalar value instead of a Vector3
    9.         float t = p.magnitude * force.magnitude * Mathf.Sin(angle) * Mathf.Rad2Deg;
    10.  
    11.         // Continuous force
    12.         if (forceMode == ForceMode2D.Force) t *= Time.fixedDeltaTime;
    13.  
    14.         // Apply inertia
    15.         return t / rigidbody2D.inertia;
    16.     }
     
    Bvenjamin and Tset_Tsyung like this.
  6. BenZed

    BenZed

    Joined:
    May 29, 2014
    Posts:
    524
    Holy damn. Rocket science. You are a rocket scientist, arn't you? You build rockets.

    Thank you so much!

    One final question, could a variant of this method be used to determine the effect on the velocity, as well?

    Something like so:

    Code (CSharp):
    1. public struct ForceResult {
    2.     public Vector velocity;
    3.     public float angularVelocity;
    4.  
    5. }
    6.  
    7. ForceResult ResultOfForceAtPosition(Vector2 force, Vector2 position, ForceMode2D forceMode = ForceMode2D.Force) {
    8.      /*partels mathematical wizardry*/
    9.     return new ForceResult();
    10. }
    I believe the result of AddForce is as simple as: velocity = force / mass * Time.fixedDeltaTime
    But the linear velocity would be reduced the more torque that is added, so if the force is not added at the center, the above velocity equation wont be correct.

    (...Right?)
     
    Last edited: Jan 19, 2015
  7. Partel-Lang

    Partel-Lang

    Joined:
    Jan 2, 2013
    Posts:
    2,554
    Building rockets is for nerds, lol :)

    mr Newton's second law of motion states that the vector sum of the forces F on an object is equal to the mass m of that object multiplied by the acceleration vector a of the object:

    F = ma

    Using that we can find that the acceleration of an object is the sum of forces acting upon it divided by it's mass:

    a = F/m

    That works for simplified uniform constant mass objects such as Rigidbodies in Unity and acceleration a should be understood as the linear acceleration of the object's center of mass.

    So linear acceleration would not be reduced by the amount of torque that results from the force as we would intuitively presume (angular velocity rotates an object about it's center of mass so it doesn't accelerate it).

    Code (CSharp):
    1. Vector2 v = force / rigidbody2D.mass;
    2.         if (forceMode == ForceMode2D.Force) v *= Time.deltaTime;
    Cheers,
    Pärtel
     
    chris_schubert and twobob like this.
  8. BenZed

    BenZed

    Joined:
    May 29, 2014
    Posts:
    524
  9. ICONS

    ICONS

    Joined:
    Aug 8, 2017
    Posts:
    1
    if you apply inverse torque... result should be zero (no turn)?

    Code (CSharp):
    1. var torque = ForceToTorque(transform.root.GetComponent<Rigidbody>(), thruster.transform.forward * z, thruster.transform.position);
    2.  
    3. transform.root.GetComponent<Rigidbody>().AddForceAtPosition(thruster.transform.forward * z, thruster.transform.position);
    4.  
    5. transform.root.GetComponent<Rigidbody>().AddRelativeTorque(-torque);
    My space ship is yet turning when i apply "z" forcé not aligned with center of mass...
     
  10. hadynlander

    hadynlander

    Joined:
    Jan 24, 2014
    Posts:
    41
    I've been wanting to use the job system to sum up a large number of small positional forces between frames without blocking the main thread, and this code example has been insanely useful to me (thus the necro). The only thing I've noticed is that I get results that are much closer to Unity's behaviour for ForceMode.VelocityChange and ForceMode.Acceleration if I modify ToDeltaTorque to always call ApplyInertiaTensor, but divide the RigidBody.inertiaTensor value by mass in cases where mass should be ignored - e.g.
    Code (CSharp):
    1. private void ToDeltaTorque(ref Vector3 torque, ForceMode forceMode)
    2. {
    3.     bool continuous = forceMode == ForceMode.Force || forceMode == ForceMode.Acceleration;
    4.     bool useMass = forceMode == ForceMode.Force || forceMode == ForceMode.Impulse;
    5.  
    6.     if (continuous) torque *= Time.fixedDeltaTime;
    7.     ApplyInertiaTensor(ref torque, useMass);
    8. }
    9.  
    10. private void ApplyInertiaTensor(ref Vector3 v, bool useMass)
    11. {
    12.     Vector3 inertiaTensor = useMass ? rb.inertiaTensor : rb.inertiaTensor / rb.mass;
    13.     v = rb.rotation * Div(Quaternion.Inverse(rb.rotation) * v, inertiaTensor);
    14. }
    I don't have a strong enough grasp on the underlying physics to know whether that makes physical sense, or if it suggests I'm doing something else wrong in how I apply the resulting torque, but it seems to work much more reliably in my testing.
     
    Last edited: Jul 20, 2022