Disclaimer: Do not optimize if your application runs at target FPS constantly on the minimum requirements machine. Move on to add new features or onto the next project. Optimizing for no reason (When it does not impact performance because your target FPS is higher than actual FPS during the entire lifecycle on a minimum requirements machine,) is not wise unless it’s intentional as an exercise to show off your fastest runtime algorithm etc.
I have written this incomplete list informed by my own professional experience with Unity, my personal projects, and referencing other blogs and unity documentation. This is not a very in-depth description of all the different topics but aims to give a brief description of most areas. Please read more from Unity tutorials to go in-depth on one area.
What’s the #1 universal way to have a bad game or application?
Answer: Bad performance. If your framerate is low / choppy / laggy then you have a bad game or application no matter what great content it contains.
How To Optimize Performance:
Dynamic vs baked-in lighting, number of rendering lights, shadow resolution can all have massive implications for performance. Dynamic lights require another set of UV coordinates to be stored for models in addition to lighting calculations. You can pre-render the baked-in lightmap and apply it to the models before final compiling to save more overhead. Because you are almost always going to want to use real-time lights and baked lighting; You can break your scene into two groups, one for static batching with static lights, and the other using dynamic as well with as few polys as possible.
Don’t play sounds that are inaudible. Monitor audio file sizes for what makes sense. Use the Audio Profiler to see details on audio CPU and memory use as well as number of sounds being played. Use correct compression settings. Switch to the detailed view to see details about individual sounds being played which is very useful if you maybe miss hearing a sound. And to confirm if sounds are working as expected.
Changing materials and shaders can be one of the most cost intensive things. See Static and Dynamic Batching and Textures for more details.
Use the Unity Profiler to diagnose the problem, and to save performance records for comparison. There are packages to extend the profiler. Great to get initial insight if it’s graphics or scripts that could be slowing your application down. Find what is the biggest unexpected bottleneck and look to optimize it. Know that while the profiler is running it adds additional overhead to performance so do not compare run timings from two different environments. Also there can be misleading information don’t take it all as gospel without research and other testing. (ex. Just display the FPS while running the application, as a countermeasure to note if the profiler overhead is related to the discrepency.)
Physics (Colliders, Triggers and Rigidbodies)
Performance here is dependent on the number of non sleeping rigidbodies and colliders in the scene. You can adjust the timestep, and maximum allowed timestep for physics updates. You don’t want to run more than 1 physics update per frame. Primitives have less overhead than meshes. Per-rigid body solver iterations to adjust the number of physics calculations for a particular object.Non-allocating queries, batch queries
Optimization Loop (Developer Strategy)
Focus on the biggest problem one at a time. Don’t optimize something that shouldn’t exist. Don’t yet focus on a problem that is negligible compared to other performance issues. Don’t extend functionality for something that may be but is not currently included. Document your performance with repeatable conditions so you will know for sure what advances you’ve made. Make your performance tests go off with one button press, any reduction in time spent here could be dramatic if you expect to run many performance tests. QA automation can also be useful here.
Optimize Scripts, Proper boxing and unboxing of variables, No large structs, First Principles, static and object oriented elements. If the Profiler can direct you to a problem script you are golden with the diagnoses and simply need to optimize the logic there.
Consider a limit on most texture sizes. Only use texture resolution necessary considering the camera distance to that graphic object. MIP maps can be used if expected to show the texture at multiple distances from the Player. Reuse textures whenever possible. Don’t apply unnecessary textures. Consider lower resolution of normal, specular maps even if you want a high resolution diffuse map on that same material. Texture property settings.
Static and Dynamic Batching
Reduce draw calls with batching where meshes are grouped together. Static for non moving GameObjects. Share materials as much as possible to reduce overhead here for the SetPass.
(Occlusion) Culling to Limit Rendering Visibility
Although it adds some overhead for the occlusion culling itself, the savings is usually worth it as you don’t want to render objects that aren’t visible and this really helps to save performance on lower-end machines. Automatically Unity does not render objects that are outside of the camera frustum, so you only need to address occlusion culling in the frustum. You use Umbra for this with a baked in process first. Generally do not use occlusion culling in exteriors, and generally yes do use it in interiors when walls would occlude much of the environment from the Player’s view. You can set the editor view to Overdraw mode to see where this occurs.
Object Pool (Game Objects) Pattern etc
Use an Object Pool to reuse Objects, Multithreading can be used to load elements in the background, Custom Garbage collection. If the Player has the option to infinitely spawn something, always have a plan around this. Don’t render or instantiate objects that are out of view. You can create an infinite city for example by moving scene objects from one side to the other as the Player moves.
Test Hardware (Optimize for Minimum Requirements Machine)
Ideally you have another computer or Mobile device that fits the minimum requirements target to run the Application. If not try to account for the difference in test machine versus minimum requirements machine you need to have the framerate be perfect from the slower machine. (Ex. you want to run the application at 60FPS, and it does run below that constantly on your machine, but on a minimum requirements machine it runs at 30FPS = no good, you need the minimum requirements machine to run at 60FPS then optimization is done.)
Corner Cases / QA Everything
Make sure to try to overload all inputs and do unexpected things as the Player to tease out any performance issues in these situations.
Obviously if you have settings not as desired here your results will be unexpected. Controls details of how graphics are rendered to whether to include debug controls.
Pause and open the Frame Debugger to get more detailed information to diagnose performance related to draw calls for that exact frame.
Acronym to help remember off-hand:
LAMP POST SCOTCH*
P Profiler (Diagnose)
O Optimization Loop (Developer Strategy)
S Static and Dynamic Batching
C (Occlusion) Culling to Limit Object Visibility
O Object Pool Pattern etc
T Test Hardware (Optimize for Minimum Requirements Machine)
C Corner Cases