4.5 Physics Inertia fix - InertiaCalculator component and editor

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):

using UnityEngine;
using System.Collections;

public class InertiaCalculator : MonoBehaviour
{
	public class MassData
	{
		public float mass, inertia;
		public Vector2 center;
	}

	public float inertia = 1f;
	public float multiplier = 1f;

	void OnEnable()
	{
		if(rigidbody2D != null)
		{
			//CALCULATE
			//You can set it up to be automatic, but this whole thing is kinda slow, so better pre-calculate inertia in editor.
			//inertia = ComputeInertia(rigidbody2D);

			//SET
			rigidbody2D.inertia = inertia * multiplier;
		}
	}

	static public float ComputeInertia(Rigidbody2D body)
	{
		//CALCULATE INERTIA, TAKING SCALING AND ALL THE CHILD COLLIDERS INTO ACCOUNT
		float hypothetic_mass = 0f;
		float hypothetic_density = 1f;
		float I = 0f;
		Vector2 center = Vector2.zero;

		Collider2D[] colliders = body.GetComponentsInChildren<Collider2D>();

		for(int i = 0;i < colliders.Length; ++i)
		{
			Collider2D c = colliders[i];
			//PICK ONLY OWN COMPS AND ADDITIONAL CHILD COLLIDERS THAT USE SAME BODY
			if(c.gameObject == body.gameObject || (c.transform.parent.gameObject == body.gameObject  c.rigidbody2D == null))
			{
				MassData data = null;

				CircleCollider2D circle = c as CircleCollider2D;
				if(circle != null) data = CircleMassInertiaCenter(circle, hypothetic_density, body.transform.position);

				BoxCollider2D box = c as BoxCollider2D;
				if(box != null) data = BoxMassInertiaCenter(box, hypothetic_density, body.transform.position);

				PolygonCollider2D poly = c as PolygonCollider2D;
				if(poly != null) data = PolygonMassInertiaCenter(poly, hypothetic_density, body.transform.position);

				if(data != null)
				{
					hypothetic_mass += data.mass;
					center += data.center * data.mass;
					I += data.inertia;
				}
			}
		}
		
		//PORTED FROM BOX2D
		// Compute the center of mass.
		center *= 1f / hypothetic_mass;

		// Center the inertia about the center of mass
		I -= hypothetic_mass * center.sqrMagnitude;
		//SCALE IT
		I *= body.mass / hypothetic_mass;

		return I;
	}
	
	static public MassData CircleMassInertiaCenter(CircleCollider2D collider, float density, Vector2 center)
	{
		//PORTED FROM BOX2D
		float r = collider.radius * collider.transform.lossyScale.x;
		float m = density * Mathf.PI * r * r;
		center = (Vector2)collider.transform.TransformPoint(collider.center) - center;
		float I = m * (0.5f * r * r + center.sqrMagnitude);
		MassData data = new MassData();
		data.inertia = I;
		data.mass = m;
		data.center = center;
		return data;
	}
	static public MassData BoxMassInertiaCenter(BoxCollider2D collider, float density, Vector2 center)
	{
		Vector2[] points = new Vector2[4];
		points[3] = -collider.size * 0.5f + collider.center;
		points[2] = new Vector2(-collider.size.x, collider.size.y) * 0.5f + collider.center;
		points[1] = collider.size * 0.5f + collider.center;
		points[0] = new Vector2(collider.size.x, -collider.size.y) * 0.5f + collider.center;

		for(int i = 0;i < points.Length; ++i) points[i] = (Vector2)collider.transform.TransformPoint(points[i]) - center;
		return PolygonMassInertiaCenter(points, density);
	}

	static public MassData PolygonMassInertiaCenter(PolygonCollider2D collider, float density, Vector2 center)
	{
		Vector2[] points = collider.points;
		for(int i = 0;i < points.Length; ++i) points[i] = (Vector2)collider.transform.TransformPoint(points[i]) - center;
		return PolygonMassInertiaCenter(points, density);
	}

	static public MassData PolygonMassInertiaCenter(Vector2[] points, float density)
	{
		//PORTED FROM BOX2D
		Vector2 center = Vector2.zero;
		float area = 0f;
		float I = 0f;
		
		// pRef is the reference point for forming triangles.
		// It's location doesn't change the result (except for rounding error).
		Vector2 p1 = Vector2.zero;
		
		float k_inv3 = 1f / 3f;

		
		for (int i = 0; i < points.Length; ++i)
		{
			// Triangle vertices.
			//b2Vec2 p1 = pRef;
			//
			//b2Vec2 p2 = m_vertices[i];
			Vector2 p2 = points[i];
			//b2Vec2 p3 = i + 1 < m_vertexCount ? m_vertices[i+1] : m_vertices[0];
			Vector2 p3 = i + 1 < points.Length ? points[i + 1] : points[0];
			
			//b2Vec2 e1 = p2 - p1;
			Vector2 e1 = p2 - p1;
			//b2Vec2 e2 = p3 - p1;
			Vector2 e2 = p3 - p1;
			
			//float32 D = b2Cross(e1, e2);
			float D = e1.x * e2.y - e1.y * e2.x;
			
			//float32 triangleArea = 0.5f * D;
			float triangleArea = 0.5f * D;
			area += triangleArea;
			
			// Area weighted centroid
			//center += triangleArea * k_inv3 * (p1 + p2 + p3);
			center += triangleArea * k_inv3 * (p1 + p2 + p3);
			
			//float32 px = p1.x, py = p1.y;
			float px = p1.x;
			float py = p1.y;
			//float32 ex1 = e1.x, ey1 = e1.y;
			float ex1 = e1.x;
			float ey1 = e1.y;
			//float32 ex2 = e2.x, ey2 = e2.y;
			float ex2 = e2.x;
			float ey2 = e2.y;

			//MAGICS
			//float32 intx2 = k_inv3 * (0.25f * (ex1*ex1 + ex2*ex1 + ex2*ex2) + (px*ex1 + px*ex2)) + 0.5f*px*px;
			float intx2 = k_inv3 * (0.25f * (ex1 * ex1 + ex2 * ex1 + ex2 * ex2) + (px * ex1 + px*ex2)) + 0.5f * px * px;
			//float32 inty2 = k_inv3 * (0.25f * (ey1*ey1 + ey2*ey1 + ey2*ey2) + (py*ey1 + py*ey2)) + 0.5f*py*py;
			float inty2 = k_inv3 * (0.25f * (ey1 * ey1 + ey2 * ey1 + ey2 * ey2) + (py * ey1 + py * ey2)) + 0.5f * py * py;
			
			I += D * (intx2 + inty2);
		}

		// Total mass
		float m = density * area;

		// Center of mass
		//b2Settings.b2Assert(area > Number.MIN_VALUE);
		//center *= 1.0f / area;
		center *= 1f / area;
		
		// Inertia tensor relative to the local origin.
		I = density * I;

		MassData data = new MassData();
		data.inertia = I;
		data.mass = m;
		data.center = center;

		return data;
	}
}

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:

using UnityEditor;
using UnityEngine;
using System;

[CanEditMultipleObjects, CustomEditor(typeof(InertiaCalculator))]
public class InertiaCalculatorEditor : Editor
{
	void OnEnable()
	{
	}

	public override void OnInspectorGUI()
	{
		DrawDefaultInspector();
		
		if(GUILayout.Button("Calculate Inertia", GUILayout.MaxWidth(200)))
		{
			for(int i = 0;i < targets.Length;++i)
			{
				InertiaCalculator calc = targets[i] as InertiaCalculator;
				if(calc != null  calc.rigidbody2D != null)
				{
					calc.inertia = InertiaCalculator.ComputeInertia(calc.rigidbody2D);
				}
			}
		}
	}
}

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.

Thanks! I can only comment on the case of a simple circle collider, but that results in much more sane default behaviour.

Yep, experience the same issue and your fix worked perfect!

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!

Thanks for this fix. It worked perfectly.

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

nm i got it! worked perfectly… thanks!!!

@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).

Why they hate us so much?? why???

Deep breath…

Ok, let’s do a downgrade.

As I’ve mentioned elsewhere, I have applied a fix for this to the next patch release.

Sorry for any problems this has caused.

1 Like

I found the thread. Thanks for inform

For my project it was fixed in 4.5.1p2

That’s good news. Glad it’s working for you.

Is that the recently released patch or one to come? I’ve just tried 4.5.1f3 and my physics is still broken.

I download from this thread

I don’t know if was already rolled out to the main download page.

I’m still having issues. the C# script above throws me errors in unity :eyes:

move info on my issue here: http://answers.unity3d.com/questions/793493/unity-45-vs-43-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