These are tips for using Rigid Bodies and Physics in Unity. I was maintaining these in a document on my PC and decided I may as well store them publicly so other people can benefit from what I have learned:
Rigidbodies and the Hierarchy
Non-kinematic rigid bodies will ignore the hierarchy. If you make one rigid body a child of another and translate the parent. Then the child will NOT move with the the parent. The child’s transform will update so that it remains in place in world space. If you want to couple rigid bodies you need to use joints.
Avoid scaling and especially non-uniform scaling of colliders. There is a big performance hit for non-uniform scaling. Negative scale makes no sense with physics so don’t. Set the scale of the GameObject to 1,1,1 and use the properties of the collider to set its size and position. Another option is to attach the collider to a child game object.
Try to separate physics geometry and render geometry in prefabs. This makes it easy to do things like LOD.
OnCollision Events
OnCollisionEnter, OnCollisionStay, OnCollisionExit events only fire on the transform with the rigidbody component on it. All colliders beneath a rigid body in the hierarchy are grouped into one big collider. If you try to capture these events with a script placed on a child collider the events will NOT fire for that script.
OnCollisionEnter, OnCollisionStay, OnCollisionExit are called in the physics loop (once per FixedUpdate call).
OnCollisionStay is NOT called if the rigidbody it is attached to is sleeping.
OnCollisionEnter, OnCollisionStay, OnCollisionExit events are called once per rigid body, per physics frame. So if a plane implements OnCollisionStay and has a cube, ball and cylinder sitting on it (each of which has a rigidbody) then OnCollisionStay will be called three times per FixedUpdate call. If the cube, ball and cylinder were all grouped in the hierarchy under a single rigid body then there would be one OnCollisionStay call.
Collisions
The Collision object contains the “collider” field which is the collider on the other object that was hit. If you want the collider on this object then look in the contacts array. There may be more than one collider in that array.
OnCollisionEnter, OnCollisionStay, OnCollisionExit calls generally occur in pairs each participant rigidbody receives a call. Most of the time the collision information is identical but WheelColliders can produce different Contacts arrays for each participant in a collision.
Collision.contacts can sometimes have zero length. Unity claims that the collision.contacts array will always have at least one value. THIS IS NOT THE CASE FOR OBJECTS WITH A COMBINATION OF REGULAR COLLIDERS AND WHEEL COLLIDERS. The following scenario can generate a frame with OnCollision calls that have zero length contact arrays. If the wheel collider suspension is being compressed over a number of frames and a regular collider grounds. Then the first frame of the regular collider hitting the ground will have collision.contacts.Length = 0.
Colliders
Contacts for mesh colliders and boxcolliders are calculated on vertices of a mesh that penetrate another collider.
- A SphereCollider will usually generate one contact point.
- A BoxCollider collider contact will usually have one,two or four contact points.
- Mesh colliders can produce hundreds of contact points. Keep them simple.
Avoid mesh colliders if possible. They are expensive. Mesh colliders must be convex to collide with other mesh colliders.
Moving And Rotating Rigid Bodies
Avoid regularly setting transform.postion and transform.rotation on rigidbodies. PhysX must do a big solve when this happens. This reduces the stability of the physics simulation and has a heavy performance cost.
The correct way to move and rotate rigidbodies is by setting the velocity and angularVelocity values.
rigidbody.transform.Translate(1,0,0); //WRONG
is equivalent to:
rigidbody.velocity = new Vector3(1f/Time.fixedDeltaTime,0,0);
Note that you need to zero the velocity on the next FixedUpdate frame to stop the rigid body.
FixedUpdate Update and Coroutines
FixedUpdate is for physics and Update is for rendering.
Fixed Update will normally be called at 60 fps.
Update will typically be called at 30 – 60 fps or more.
When using coroutines for physics calculations be sure to use WaitForFixedUpdate NOT WaitForUpdate. Update is for rendering, Fixed update is for physics.
Floating Point Precision Errors
Keep your game within 6000 units of the origin. Floating point precision drops the further from the origin you get. You will start to notice glitches if you get too far from the origin. This limit seems to be around 6000 units.
Keep your forces below 10,000 Newtons. Forces above this can cause instability.
Keep your torques even smaller. PhysX clamps angularVelocity (The setting is in the Physics settings). There is no point applying a torque that will accelerate more that this limit in a single frame.
Keep your velocities within 1000 units of zero. If you need to go faster than this then use a moving reference frame.
Keep your masses bellow 10000 kg and above .01 kg. Jittering and instability may result if you push this too far.
PD Controllers
This is a large topic I will write a separate page on it.