Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

4.5 Physics Inertia fix - InertiaCalculator component and editor

Discussion in '2D' started by vapgames, May 28, 2014.

  1. vapgames

    vapgames

    Joined:
    Aug 25, 2012
    Posts:
    9
    Hi,
    As some people who work with 2d physics probably noticed, inertia in 4.5 update is calculated incorrectly.
    So, here's a component based on box2d inertia code that calculates it correctly (at least more correctly than it is now):
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class InertiaCalculator : MonoBehaviour
    6. {
    7.     public class MassData
    8.     {
    9.         public float mass, inertia;
    10.         public Vector2 center;
    11.     }
    12.  
    13.     public float inertia = 1f;
    14.     public float multiplier = 1f;
    15.  
    16.     void OnEnable()
    17.     {
    18.         if(rigidbody2D != null)
    19.         {
    20.             //CALCULATE
    21.             //You can set it up to be automatic, but this whole thing is kinda slow, so better pre-calculate inertia in editor.
    22.             //inertia = ComputeInertia(rigidbody2D);
    23.  
    24.             //SET
    25.             rigidbody2D.inertia = inertia * multiplier;
    26.         }
    27.     }
    28.  
    29.     static public float ComputeInertia(Rigidbody2D body)
    30.     {
    31.         //CALCULATE INERTIA, TAKING SCALING AND ALL THE CHILD COLLIDERS INTO ACCOUNT
    32.         float hypothetic_mass = 0f;
    33.         float hypothetic_density = 1f;
    34.         float I = 0f;
    35.         Vector2 center = Vector2.zero;
    36.  
    37.         Collider2D[] colliders = body.GetComponentsInChildren<Collider2D>();
    38.  
    39.         for(int i = 0;i < colliders.Length; ++i)
    40.         {
    41.             Collider2D c = colliders[i];
    42.             //PICK ONLY OWN COMPS AND ADDITIONAL CHILD COLLIDERS THAT USE SAME BODY
    43.             if(c.gameObject == body.gameObject || (c.transform.parent.gameObject == body.gameObject  c.rigidbody2D == null))
    44.             {
    45.                 MassData data = null;
    46.  
    47.                 CircleCollider2D circle = c as CircleCollider2D;
    48.                 if(circle != null) data = CircleMassInertiaCenter(circle, hypothetic_density, body.transform.position);
    49.  
    50.                 BoxCollider2D box = c as BoxCollider2D;
    51.                 if(box != null) data = BoxMassInertiaCenter(box, hypothetic_density, body.transform.position);
    52.  
    53.                 PolygonCollider2D poly = c as PolygonCollider2D;
    54.                 if(poly != null) data = PolygonMassInertiaCenter(poly, hypothetic_density, body.transform.position);
    55.  
    56.                 if(data != null)
    57.                 {
    58.                     hypothetic_mass += data.mass;
    59.                     center += data.center * data.mass;
    60.                     I += data.inertia;
    61.                 }
    62.             }
    63.         }
    64.        
    65.         //PORTED FROM BOX2D
    66.         // Compute the center of mass.
    67.         center *= 1f / hypothetic_mass;
    68.  
    69.         // Center the inertia about the center of mass
    70.         I -= hypothetic_mass * center.sqrMagnitude;
    71.         //SCALE IT
    72.         I *= body.mass / hypothetic_mass;
    73.  
    74.         return I;
    75.     }
    76.    
    77.     static public MassData CircleMassInertiaCenter(CircleCollider2D collider, float density, Vector2 center)
    78.     {
    79.         //PORTED FROM BOX2D
    80.         float r = collider.radius * collider.transform.lossyScale.x;
    81.         float m = density * Mathf.PI * r * r;
    82.         center = (Vector2)collider.transform.TransformPoint(collider.center) - center;
    83.         float I = m * (0.5f * r * r + center.sqrMagnitude);
    84.         MassData data = new MassData();
    85.         data.inertia = I;
    86.         data.mass = m;
    87.         data.center = center;
    88.         return data;
    89.     }
    90.     static public MassData BoxMassInertiaCenter(BoxCollider2D collider, float density, Vector2 center)
    91.     {
    92.         Vector2[] points = new Vector2[4];
    93.         points[3] = -collider.size * 0.5f + collider.center;
    94.         points[2] = new Vector2(-collider.size.x, collider.size.y) * 0.5f + collider.center;
    95.         points[1] = collider.size * 0.5f + collider.center;
    96.         points[0] = new Vector2(collider.size.x, -collider.size.y) * 0.5f + collider.center;
    97.  
    98.         for(int i = 0;i < points.Length; ++i) points[i] = (Vector2)collider.transform.TransformPoint(points[i]) - center;
    99.         return PolygonMassInertiaCenter(points, density);
    100.     }
    101.  
    102.     static public MassData PolygonMassInertiaCenter(PolygonCollider2D collider, float density, Vector2 center)
    103.     {
    104.         Vector2[] points = collider.points;
    105.         for(int i = 0;i < points.Length; ++i) points[i] = (Vector2)collider.transform.TransformPoint(points[i]) - center;
    106.         return PolygonMassInertiaCenter(points, density);
    107.     }
    108.  
    109.     static public MassData PolygonMassInertiaCenter(Vector2[] points, float density)
    110.     {
    111.         //PORTED FROM BOX2D
    112.         Vector2 center = Vector2.zero;
    113.         float area = 0f;
    114.         float I = 0f;
    115.        
    116.         // pRef is the reference point for forming triangles.
    117.         // It's location doesn't change the result (except for rounding error).
    118.         Vector2 p1 = Vector2.zero;
    119.        
    120.         float k_inv3 = 1f / 3f;
    121.  
    122.        
    123.         for (int i = 0; i < points.Length; ++i)
    124.         {
    125.             // Triangle vertices.
    126.             //b2Vec2 p1 = pRef;
    127.             //
    128.             //b2Vec2 p2 = m_vertices[i];
    129.             Vector2 p2 = points[i];
    130.             //b2Vec2 p3 = i + 1 < m_vertexCount ? m_vertices[i+1] : m_vertices[0];
    131.             Vector2 p3 = i + 1 < points.Length ? points[i + 1] : points[0];
    132.            
    133.             //b2Vec2 e1 = p2 - p1;
    134.             Vector2 e1 = p2 - p1;
    135.             //b2Vec2 e2 = p3 - p1;
    136.             Vector2 e2 = p3 - p1;
    137.            
    138.             //float32 D = b2Cross(e1, e2);
    139.             float D = e1.x * e2.y - e1.y * e2.x;
    140.            
    141.             //float32 triangleArea = 0.5f * D;
    142.             float triangleArea = 0.5f * D;
    143.             area += triangleArea;
    144.            
    145.             // Area weighted centroid
    146.             //center += triangleArea * k_inv3 * (p1 + p2 + p3);
    147.             center += triangleArea * k_inv3 * (p1 + p2 + p3);
    148.            
    149.             //float32 px = p1.x, py = p1.y;
    150.             float px = p1.x;
    151.             float py = p1.y;
    152.             //float32 ex1 = e1.x, ey1 = e1.y;
    153.             float ex1 = e1.x;
    154.             float ey1 = e1.y;
    155.             //float32 ex2 = e2.x, ey2 = e2.y;
    156.             float ex2 = e2.x;
    157.             float ey2 = e2.y;
    158.  
    159.             //MAGICS
    160.             //float32 intx2 = k_inv3 * (0.25f * (ex1*ex1 + ex2*ex1 + ex2*ex2) + (px*ex1 + px*ex2)) + 0.5f*px*px;
    161.             float intx2 = k_inv3 * (0.25f * (ex1 * ex1 + ex2 * ex1 + ex2 * ex2) + (px * ex1 + px*ex2)) + 0.5f * px * px;
    162.             //float32 inty2 = k_inv3 * (0.25f * (ey1*ey1 + ey2*ey1 + ey2*ey2) + (py*ey1 + py*ey2)) + 0.5f*py*py;
    163.             float inty2 = k_inv3 * (0.25f * (ey1 * ey1 + ey2 * ey1 + ey2 * ey2) + (py * ey1 + py * ey2)) + 0.5f * py * py;
    164.            
    165.             I += D * (intx2 + inty2);
    166.         }
    167.  
    168.         // Total mass
    169.         float m = density * area;
    170.  
    171.         // Center of mass
    172.         //b2Settings.b2Assert(area > Number.MIN_VALUE);
    173.         //center *= 1.0f / area;
    174.         center *= 1f / area;
    175.        
    176.         // Inertia tensor relative to the local origin.
    177.         I = density * I;
    178.  
    179.         MassData data = new MassData();
    180.         data.inertia = I;
    181.         data.mass = m;
    182.         data.center = center;
    183.  
    184.         return data;
    185.     }
    186. }
    187.  
    188.  
    There's a multiplier property, so you can set inertia proportionally higher or lower.


    And here's an editor that adds "Calculate Inertia" button to inspector:
    Code (csharp):
    1.  
    2. using UnityEditor;
    3. using UnityEngine;
    4. using System;
    5.  
    6. [CanEditMultipleObjects, CustomEditor(typeof(InertiaCalculator))]
    7. public class InertiaCalculatorEditor : Editor
    8. {
    9.     void OnEnable()
    10.     {
    11.     }
    12.  
    13.     public override void OnInspectorGUI()
    14.     {
    15.         DrawDefaultInspector();
    16.        
    17.         if(GUILayout.Button("Calculate Inertia", GUILayout.MaxWidth(200)))
    18.         {
    19.             for(int i = 0;i < targets.Length;++i)
    20.             {
    21.                 InertiaCalculator calc = targets[i] as InertiaCalculator;
    22.                 if(calc != null  calc.rigidbody2D != null)
    23.                 {
    24.                     calc.inertia = InertiaCalculator.ComputeInertia(calc.rigidbody2D);
    25.                 }
    26.             }
    27.         }
    28.     }
    29. }
    30.  
    So just add this component to every body, and press the "Calculate Inertia" in editor every time you update the shapes.

    BECAUSE 4.5 BROKE ALL MY PHYSICS AND I'M NOT GONNA WAIT MONTHS TILL IT'S FIXED.
     
    Last edited: May 28, 2014
  2. mostlytigerproof

    mostlytigerproof

    Joined:
    Jan 8, 2014
    Posts:
    6
    Thanks! I can only comment on the case of a simple circle collider, but that results in much more sane default behaviour.
     
  3. jeffweber

    jeffweber

    Joined:
    Dec 17, 2009
    Posts:
    616
    Yep, experience the same issue and your fix worked perfect!
     
  4. pumpkinszwan

    pumpkinszwan

    Joined:
    Feb 6, 2014
    Posts:
    214
    Thanks for this! I had figured out a simple way to correct the inertia on all my objects, but was having a hard time figuring out the correct inertia values through trial and error...with your scripts I am able to calculate the correct values and stick them into my prefabs.

    Thanks!
     
  5. halfbot

    halfbot

    Joined:
    Sep 22, 2013
    Posts:
    19
    Thanks for this fix. It worked perfectly.
     
  6. azaqero

    azaqero

    Joined:
    Mar 6, 2014
    Posts:
    4
    hello. how can i use this code? i created 2 scripts... should add InertiaCalculator to each game object affected by 2d physics? and how add the editor code? thanks
     
  7. azaqero

    azaqero

    Joined:
    Mar 6, 2014
    Posts:
    4
    nm i got it! worked perfectly.. thanks!!!!
     
  8. slippdouglas

    slippdouglas

    Joined:
    Feb 11, 2013
    Posts:
    23
    @vapgames: In dire situations like yours I would also consider just going back to 4.3 (assuming you have project checked-in/backed-up prior to installing 4.5).
     
  9. Sisso

    Sisso

    Joined:
    Sep 29, 2009
    Posts:
    196
    Why they hate us so much?? why????

    Deep breath..

    Ok, let's do a downgrade.
     
  10. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,325
    As I've mentioned elsewhere, I have applied a fix for this to the next patch release.

    Sorry for any problems this has caused.
     
    timdan likes this.
  11. Sisso

    Sisso

    Joined:
    Sep 29, 2009
    Posts:
    196
    I found the thread. Thanks for inform
     
  12. Sisso

    Sisso

    Joined:
    Sep 29, 2009
    Posts:
    196
    For my project it was fixed in 4.5.1p2
     
  13. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,325
    That's good news. Glad it's working for you.
     
  14. SoftwareGeezers

    SoftwareGeezers

    Joined:
    Jun 22, 2013
    Posts:
    902
    Is that the recently released patch or one to come? I've just tried 4.5.1f3 and my physics is still broken.
     
  15. Sisso

    Sisso

    Joined:
    Sep 29, 2009
    Posts:
    196
  16. Tekksin

    Tekksin

    Joined:
    Oct 20, 2013
    Posts:
    32
    I'm still having issues. the C# script above throws me errors in unity o_O

    move info on my issue here: http://answers.unity3d.com/question...player-movement-stuttering.html#answer-793571

    errors from this script include:
    Assets/Scripts/InertiaCalculator.cs(42,122): error CS1525: Unexpected symbol `c'
    Assets/Scripts/InertiaCalculator.cs(66,33): error CS1519: Unexpected symbol `*=' in class, struct, or interface member declaration
    Assets/Scripts/InertiaCalculator.cs(66,55): error CS1519: Unexpected symbol `;' in class, struct, or interface member declaration

    etc
     
    Last edited: Sep 19, 2014