This is the fifth in a series of articles that unpacks optimization tips for your Unity projects. Use them as a guide for running at higher frame rates with fewer resources. Once you’ve tried these best practices, be sure to check out the other pages in the series:
- Configuring your Unity project for stronger performance
- Performance optimization for high-end graphics
- Managing GPU usage for PC and console games
- Advanced programming and code architecture
Physics can create intricate gameplay, but this comes with a performance cost. Once you know these costs, you can tweak the simulation to manage them appropriately. Use these tips to stay within your target frame rate and create smooth playback with Unity’s built-in Physics (NVIDIA PhysX).
Meshes used in physics go through a process called Cooking. This prepares the mesh so that it can work with physics queries such as raycasts, contacts, and so on.
A MeshCollider has several CookingOptions to help you validate the mesh for physics. If you’re certain that your mesh does not need these checks, you can disable them to speed up your cook time.
In the CookingOptions for each MeshCollider, uncheck the EnableMeshCleaning, WeldColocatedVertices, and CookForFasterSimulation. These options are valuable for procedurally-generated meshes at runtime, but can be disabled if your meshes already have the proper triangles.
If you’re targeting PC specifically, make sure to keep Use Fast Midphase enabled. This switches to a faster algorithm from PhysX 4.1 during the midphase of the simulation (which helps narrow down a small set of potentially intersecting triangles for physics queries). Non-desktop platforms must still use the slower algorithm that generates R-trees.
Mesh Colliders are generally expensive, so consider substituting more complex Mesh Colliders with primitive or simplified ones to approximate the original shape.
Learn more in the CookingOptions documentation.
If you are generating meshes procedurally during gameplay, you can create a Mesh Collider at runtime. Adding a Mesh Collider component directly to the mesh, however, cooks the physics on the main thread. This can consume significant CPU time.
Use Physics.BakeMesh to prepare a mesh for use with a Mesh Collider, and save the baked data with the mesh itself. A new Mesh Collider referencing this mesh will reuse this prebaked data, rather than baking the mesh again. This can help reduce scene load time or instantiation time later. To optimize performance, you can offload mesh cooking to another thread with the C# Job System.
Refer to this example for details on how to bake meshes across multiple threads.
In the Player Settings, check Prebake Collision Meshes whenever possible. We also recommend reviewing the Collision Matrix setup to ensure that the player and game mechanic objects are in the correct layers.
Removing callbacks from triggers for unnecessary layers can be a big win, so try to simplify your Layer Collision Matrix. You can edit your Physics Settings via Project Settings > Physics.
Learn more in the Collision Matrix documentation.
Physics engines work by running on a Fixed Timestep. To see the fixed rate that your project is running at, go to Edit > Project Settings > Time.
The Fixed Timestep field defines the time delta used by each Physics Step. For example, the default value of 0.02 seconds (20 ms) is equivalent to 50 frames per second (fps), or 50 Hz.
Seeing as each frame in Unity takes a variable amount of time, it is not perfectly synced with the physics simulation. The engine counts up to the next physics Timestep. If a frame runs slightly slower or faster, Unity uses the elapsed time to know when to run the physics simulation at the proper Timestep.
In the event that a frame takes a long time to prepare, this can lead to performance issues. So if your game experiences a spike (e.g., instantiating many GameObjects or loading a file from disk), the frame could take 40 ms or more to run. With the default 20 ms Fixed Timestep, this would cause two physics simulations to run on the following frame to “catch up” to the variable Timestep.
Extra physics simulations, in turn, add more time to process the frame. On lower-end platforms, this potentially leads to a downward spiral of performance.
A subsequent frame taking longer to prepare makes the backlog of physics simulations longer as well. This leads to even slower frames and more simulations to run per frame. The result is weakened performance.
Eventually, the time between physics updates could exceed the Maximum Allowed Timestep. After this cutoff, Unity starts dropping physics updates, and the game stutters.
To avoid performance issues with physics:
- Reduce the simulation frequency: For low-end platforms, increase theFixed Timestep to slightly more than your target frame rate. For example, use 0.035 seconds for 30 fps on mobile. This could help prevent that downward performance spiral.
- Decrease the Maximum Allowed Timestep: Using a smaller value (like 0.1 seconds) will sacrifice some physics simulation accuracy, but also limit how many physics updates can take place in one frame. Experiment with values to find something that works for your project’s requirements.
- Simulate the Physics Step manually if necessary: Disable the Auto Simulation option in the Physics Settings, and directly invoke Physics.Simulate during the Update phase of the frame. This allows you to actively determine when to run the Physics Step. Pass Time.deltaTime to Physics.Simulatein order to keep the physics in sync with the simulation time.
- Note: This approach can cause instabilities in the physics simulation – especially in scenes with complex physics or highly variable frame times. Use it with caution.
Learn more in the Physics.Simulate documentation.
The Unity physics engine runs in two steps:
- The broad phase: Collects potential collisions using a Sweep and Prune algorithm
- The narrow phase: When the engine actually computes the collisions
The broad phase default setting of Sweep and Prune Broadphase (Edit > Project Settings > Physics > Broadphase Type) can generate false positives for worlds that are generally flat, and have many colliders. If your scene is large and mostly flat, avoid this issue and switch to Automatic Box Pruning or Multibox Pruning Broadphase. These options divide the world into a grid, where each grid cell performs Sweep and Prune.
Multibox Pruning Broadphase allows you to specify the world boundaries and the number of grid cells manually, while Automatic Box Pruning calculates it all for you.
See the full list of Physics properties here.
If you want to simulate a specific physics body more accurately, increase its Rigidbody.solverIterations. This overrides the Physics.defaultSolverIterations, which can be found via Edit > Project Settings > Physics > Default Solver Iterations.
To optimize your physics simulations, set a relatively low value in the project’s defaultSolveIterations. Then apply higher custom Rigidbody.solverIterations values to the individual instances that need more detail.
Get more information on Rigidbody.solverIterations.
When you update a Transform, Unity does not automatically sync it to the physics engine. Unity accumulates transformations and waits for either the physics update to execute or for the user to call Physics.SyncTransforms.
If you want to sync physics with your Transforms more frequently, you can set Physics.autoSyncTransform to True (also found in Project Settings > Physics > Auto Sync Transforms). When this is enabled, any Rigidbody or Collider on that Transform, along with its children, automatically update with the Transform.
However, if this is not absolutely necessary, disable it. A series of successive physics queries (such as raycasts) can lead to a loss in performance.
Find out more about Physics.SyncTransforms.
Collision and Trigger events (OnCollisionEnter, OnCollisionStay, OnCollisionExit, OnTriggerEnter, OnTriggerStay, OnTriggerExit) will fire for any MonoBehaviour that implements these functions and fulfills the interaction criteria. These events will also be sent to disabled MonoBehaviours.
That’s why it’s recommended to only implement these functions when needed, as empty, nonessential functions will be called. Special care must be taken with OnCollisionStay and OnTriggerStay because these can be called multiple times depending on the number of colliders involved.
The callbacksMonoBehaviour.OnCollisionEnter, MonoBehaviour.OnCollisionStay, and MonoBehaviour.OnCollisionExit also take a collision instance as a parameter. This collision instance is allocated on the managed heap and must be garbage collected.
To reduce the amount of garbage generated, enable Physics.reuseCollisionCallbacks(found in Project Settings > Physics > Reuse Collision Callbacks). With this active, Unity only assigns a single collision pair instance to each callback. This reduces waste for the garbage collector (GC) and improves overall performance.
Note: If you reference the collision instance outside of the Collision Callbacks for post-processing, you must disableReuse Collision Callbacks.
Learn more about Physics.reuseCollisionCallbacks.
Static Colliders are GameObjects with a Collider component but without a Rigidbody. Contrary to its name, you can move a Static Collider around.
Simply modify the position of the physics body, accumulate the positional changes, and sync before the physics update.You don’t need to add a Rigidbody component to the Static Collider just to move it. But if you want the Static Collider to interact with other physics bodies in a more complex way, give it a kinematic Rigidbody. Use Rigidbody.position and Rigidbody.rotation to move it instead of accessing the Transform component. This guarantees more predictable behavior from the physics engine.
Note: In 2D physics, do not move Static Colliders because the tree rebuild is time-consuming.
Get more information about Rigidbodies.
To detect and collect colliders within a certain distance, in a particular direction, use raycasts and other physics queries like BoxCast.
Physics queries that return multiple colliders as an array, like OverlapSphere or OverlapBox, need to allocate those objects on the managed heap. This means that the garbage collector eventually needs to collect the allocated objects, which can decrease performance if it happens at the wrong time.
To reduce this overhead, use the NonAlloc versions of those queries. For example, if you are using OverlapSphere to collect all potential colliders around a point, be sure to use OverlapSphereNonAlloc. This allows you to pass in an array of colliders (the resultsparameter) to act like a buffer.
The NonAlloc method works without generating garbage. Otherwise, it functions like the corresponding allocating method. Just remember to define a results buffer of sufficient size when using a NonAlloc method. The buffer does not grow if it runs out of space.
Learn more in the NonAlloc method documentation.
Although you can run raycast queries with Physics.Raycast, it can take a significant amount of CPU time. This is especially true if you have a large number of raycast operations (e.g., calculating line of sight for 10,000 agents).
Use RaycastCommand to batch the query using the C# Job System. This offloads work from the main thread so that the raycasts can occur asynchronously and in parallel.
See an example in the RaycastCommands documentation.
Use the Physics Debug window (Window > Analysis > Physics Debugger) to help troubleshoot any problem colliders or discrepancies. This window shows a color-coded indicator of GameObjects that can collide with one another.
For more information, see the Physics Debug visualization page.
One of our most comprehensive guides ever collects over 80 actionable tips on how to optimize your games for PC and console. Created by our expert Success and Accelerate Solutions engineers, these in-depth tips will help you get the most out of Unity and boost your game’s performance.