Fixed TimeStep

The physics in Unity (and most physics engines) runs at a fixed timestep. This is so that it can accurately make collision predictions, no matter what the framerate is. By default, in a new Unity project, it runs at 50 times per second. Internally, this works by re-calling the physics update X times if the time between two frames was higer than X times the time of the fixed timestep. You can change the fixed timestep value in the Project Settings > Time window.

This means that depending on your framerate, the FixedUpdate() could happen multiple times, or no times at all between two Update().

This is important to consider at all times for all sorts of things, but most notably because it often affects the smoothness of the movement. In the following example, the green capsule is moved on Update(), the yellow capsule is moved on FixedUpdate() with an interpolated Rigidbody, and the blue capsule is moved on FixedUpdate() without interpolation. (note that the fixed timestep has been exaggerated to properly see the difference in a low-framerate gif). This happens because the Update() and rendering of the scene is done at 100 fps for example, but the FixedUpdate() only runs at 50 fps.

Unity devs often make a very common mistake when making Rigidbody Character Controllers; they are unaware that their character's movement only runs at a fixed 50 fps, and this results in motion that feels slightly choppy and unresponsive compared to the rest of the game even if they can't quite put their finger on it. In order to make physics movement not choppy, you'll need to understand Rigidbody interpolation and all the ways it can be broken when you think it's supposed to be working...

Rigidbody Interpolation

Rigidbody interpolation only works under specific conditions, depending on the properties of your Rigidbody and what methods you use to make it move.

If your Rigidbody is Kinematic...

For interpolation to work with kinematic rigidbodies, you need to handle ALL movement through rigidbody.MovePosition() and rigidbody.MoveRotation() during the FixedUpdate(). If you make any movement or rotation with transform.position, rigidbody.position, etc.... at any point in the Update() or FixedUpdate() loop, the interpolation will be broken.

However, you are allowed to move the rigidbody.position, do some operation (like a sweep test from a backstepped position), and replace the rigidbody.position at its original position within a FixedUpdate(), and then do a MovePosition(). The interpolation will still work.

If your Rigidbody is non-Kinematic...

For interpolation to work with non-kinematic rigidbodies, you need to handle ALL movement through rigidbody.velocity, rigidbody.AddForce(), rigidbody.angularVelocity and rigidbody.AddTorque() during the FixedUpdate(). If you make any movement or rotation with transform.position, rigidbody.position, rigidbody.MovePosition(), etc.... at any point in the Update() or FixedUpdate() loop, the interpolation will be broken. For instance, if you handle all position changes with rigidbody.velocity but orient the transform with transform.rotation at some point, there will be no interpolation at all.

When all else fails....

In theory, it's always possible to make interpolation work. But if you truly need smooth, frame-perfect physics behaviour and cannot make interpolation work for your purposes, you can always set the fixed timestep to a value that represents the max refresh rate of the monitor, or the locked framerate value (60 Hz, 75 Hz, 144Hz, etc...). But keep in mind that this should only be a last resort as it will make the physics processing proportionally more expensive. You could, for example, make the user select their target framerate in a game menu (several games do this, like GTA5), and then set the fixedTimeStep rate and the locked framerate to this value. Just remember to disable interpolation everywhere if you choose to do this.

Collision events

You are most likely familiar with the basic workings of the OnCollisionEnter() and OnTriggerEnter() calls, but there are several additional things you should know about them:

  • In the execution order of a frame, they happen after the FixedUpdate(), and after the actual collision has been resolved by the physics engine (after the colliders have been pushed away from eachother).
  • These events are fired only under these circumstances:
  • These events are fired per-collider in a hierarchy. Meaning that if you have an ObjectA with a rigidbody and a SphereCollider, and a child ObjectB with a BoxCollider, a Monobehaviour on ObjectB will only get the callbacks for the BoxCollider, while a Monobehaviour on ObjectA will get the callbacks for the Box and the Sphere.

Physics queries

Things to know about Raycast, SphereCast, BoxCast, CapsuleCast...

  • They will not return a valid hit if their casting volume starts out already intersecting with the collider. So for instance, if you do a SphereCast and a tiny part of the defined sphere is already inside another collider, the cast won't return a hit with this collider.
  • Do not use RaycastAll, SpherecastAll, etc..... because they create garbage. Use RaycastNonAlloc, SpherecastNonAlloc, etc.... instead
  • They can give inaccurate results against concave colliders

Things to know about OverlapSphere, OverlapBox, OverlapCapsule...

  • Do not use any of these methods, because they create garbage. Use OverlapSphereNonAlloc, OverlapBoxNonAlloc, OverlapCapsuleNonAlloc instead

Rigidbody.SweepTest()

  • SweepTest() takes the entire collision volume of a rigidbody (all box, sphere, capsule, and convex mesh colliders under that rigidbody) and does a "cast" with this volume in the given direction. Very useful to determine if a complex object would collide with anything after a given movement.
  • Doesn't include child rigidbodies in its sweep
  • SweepTestAll() generates garbage, but there is currently no way around it. A fix will come eventually when Unity moves to PhysX 3.4

Physics.ComputePenetration()

  • This method is very useful to use in conjunction with Overlap methods in order to get collision information at any point in the frame, without relying on OnCollision callbacks. It is also a good way to do custom collision handling for kinematic rigidbodies. It returns the collision normal and penetration distance. You can then use this information to push the rigidbody away from the collision.
  • There are bugs (Unity 5.6) when using this against colliders that have a non-zero center and are scaled or rotated.

results matching ""

    No results matching ""