This tutorial provides some practical guidance on how to optimize your Unity environments to run well, with a particular focus on low end systems e.g. integrated graphics / mobile systems, or VR.
In a recent class of mine on some on some old integrated graphics based machines we were able to increase the frame rate the student was experiencing from 2 fps to about 70 fps in just a few minutes, and with more time and focus and following all of the tips shared here we could have done better.
Unfortunately there is no silver bullet to optimization as every game is different. The approach I take is to experiment with one setting at at time and keep experimenting and profiling until I get the settings I am happy with.
Firstly, some cautionary of notes on optimization:
- What is your target audience going to be using at the time you think your game will release? Graphical power is the area in mobile and desktop that is advancing at the greatest pace. Knowing your target hardware will determine how much time you need to spend optimizing.
- Time is money – and optimization is a game of sometimes frustratingly diminishing returns. Solve the big problems first and then get on with making your game. If you don’t need to do it then don’t waste your time!
- Optimize early and often. Get a sense for how much content you can jam into your scene, what assets and post fx you can use then stick to it. You will lose time later through re-work if you don’t pay attention now.
- The profiler is your friend. Try stuff and keep the things that work. Be aware that running the editor and the profiler pollutes your results and reduces framerate due to the overhead of measuring and reporting what is going on. In fact, even running the project in your editor pollutes the results.
- Optimization is all about making tradeoffs – you will most likely lose some visual quality in the process – understanding this, and being artful in the way you hide these trade-offs is is a key part of making games. For example, use fog to hide things in the distance and then render lower LOD’s or not at all. Its technically worse, but visually your audience will never know.
The best way to profile is on a compiled build. Be sure you disable vsync to stop it from influencing your profile. However even profiling in the editor is enough to show you where the bottlenecks are likely to be.
Here are some tips to explore. Some are mobile specific and some are more general. Pick and choose whats good for you:
- Spawn less stuff. Simple eh! The more you put into your scene the more expensive it is to render. If you have performance issues, consider what you can cut from your scene. You will not get a desktop scene to run well on mobile if it has too much content for the target device.
- But I want an infinitely massive open world? Better sharpen your skills – this is hard to do even for teams with experienced pro’s and it requires deep understanding of the engine and how to get the best out of it. If you are new to game development then focus your attention on smaller environments and filling them with great game play.
- Unity terrain is expensive as it is a memory and resource hog. The higher the resolution, the further the detail distance, the worse your performance will be. You can find your resolution settings in the Gaia Defaults file. Check the bottom of this post for some sample mobile settings. Here are some general rules of thumb:
- Use lower resolutions and ensure everything is a power of 2
- My rule of thumb for desktops is to make all the resolutions half of the terrain size.
- For mobile you might make them a quarter or even and eighth of the terrain size.
- Make your terrains smaller and consider a tool like World Streamer that will only load the terrain if its in view.
- Use less textures – a maximum of 4 on low end systems
- Use less different grasses – each additional grass consumes memory, rendering and culling overhead
- Drop the size on your textures and normal maps to be more low end friendly. Experiment with 256 and 512 sized textures. The textures supplied with Gaia are located in Gaia / 3rd Party Samplers / GameTexturesDotCom. You are often better to pre-optimize your textures with specialist 3rd party tools than to let Unity do it for you as they will most likely be cleaner and this will have a substantial visual impact.
- Use a maximum of 4 textures on your terrain. Every multiple of 4 textures causes the unity terrain shader to render an additional pass (draw the terrain all over again). So even adding 1 texture over this boundary of 4 will cause another pass.
- Run a smooth or two on your terrain after you have finished stamping. Smoothing the terrain reduces the number of polys that need to be rendered.
- Increase the Pixel error on your terrain. This will have the effect of reducing more distant terrain at a lower resolution (LOD), but will also increase visual artifacts as you move around. Experiment until you find a trade off you are happy with.
- Either lose grass completely, or use it sparingly. It is a performance hog. In particular it will cause jitter issues when it is culled. You can shrink the detail distance in your terrain settings, and also lessen the detail density. Make sure that you color your grass so that it blends with the color of the terrain – this way you lessen the visual impact of hitting the detail distance.
- By default the camera will use frustrum culling to remove objects that are out of view on either side of the current camera view, however you can get another level of performance as well by also using occlusion culling. Occlusion culling will stop objects that are behind other objects from rendering and will result in more objects being culled than just frustrum culling. Rendering less objects results means less draw calls and higher performance. You can bake your occlusion culling via the occlusion culling window to generally get an immediate performance improvement. TIP: Be creative during level design and use of larger objects such as mountains, hills and walls in your scene. These will block your view of distant objects, and hence increase your framerate via culling.
- Reduce your shadow distance for each of the quality settings you use. If you can get away with it, use hard shadows. You will find these under Edit -> Project Settings -> Quality : Shadow Distance. Also explore the other shadow settings while you are there. Click on Game view and experiment to get the visual settings you want.
- Reduce your LOD bias for each quality setting you support. You will find these under Edit -> Project Settings -> Quality : Lod Bias. Click on Game view and experiment to get the visual settings you want.
- Use Graphical Post FX sparingly. Profile the FX to validate their impact on performance. In later versions of Unity you can see these at design time. Click on Game view and experiment to get the visual settings you want.
- Use mobile optimized trees. The tree samples provided with Gaia are desktop trees. If using SpeedTree, experiment with your LOD and wind settings. Preferably lose wind completely.
- Use mobile optimized assets in general. The assets that come with Gaia are good for desktop, but not so great for mobile. Gaia will happily work with whatever assets you throw at it, so you can easily swap your own in.
- Lose wind completely – delete the Wind Zone object that Gaia creates. You will find it under Gaia Environment / Wind Zone in the scene hierarchy.
- Lose Unity water completely. The default Unity water is very inefficient. If you must use Unity water then swap it out for the WaterBasicDaytime version that comes in Standard Assets / Environment / Water Basic. Better yet purchase a good quality 3rd party water shader optimized for mobile such as AQUAS.
- Make sure you mark rocks and other assets that wont move as static, this will allow unity to batch them, which in turn reduces draw calls and increases frame rate. Make sure when you spawn these assets that their scale is a whole number and this will allow Unity to choose the best case between dynamic and static batching.
- Instance as much as you can. Instancing is the process of sending the object to the GPU once, and then rendering it multiple times, as compared to sending it to the GPU for every single time it needs to be rendered. Check this article out on Instancing SpeedTree.
- In your lighting tab, select either Precomputed Realtime GI, or Baked GI, but not both. You will need to update the lights in your scene to reflect the settings you have chosen.
- In your lighting tab, make your ambient light source a fixed color. This is less GPU intensive than using the Skybox.
- In your lighting tab, make your sky a non procedural Skybox instead of the default procedural Skybox. Its much cheaper often looks better.
- Use baked lighting if you can get away with it. The means you lose the benefit of time of day lighting, but you get it back in performance. If you remove shadows and other lighting calculations then this reduces the amount of work the device has to do.
- Lighting tip : Often in editor mode you will have objects that look really dark. Usually they will display correctly only after you have finished the lighting bake. Be sure to set objects that don’t move to static so that they will be affected correctly by lighting.
- Use Forward instead of Deferred rendering path. This is cheaper for environments that have a single light e.g. the sun. It will perform words however if there are multiple lights in the scene. You will find this setting under Player Settings. File -> Build Settings -> Player Settings. If you have multiple lights however, Deferred will be faster and better.
- Select the “Fastest” setting under Quality settings. Edit -> Project Settings -> Quality. Alternatively you can also attach the FrameRateManager script in the Gaia / Scripts / ValueAdds directory to a static game object such as the terrain and it will dynamically change your environmental settings to try and maintain a given frame rate. You will need to modify its settings in the script to suit the type of game and the device you are supporting.
- If you do need large environments, use World Streamer. It breaks the terrains down into smaller chunks, and culls terrains that are not in view. It can make a dramatic performance difference even on desktop games.
- In your scripts, never leave the Update method if the script does nothing – this chews up cpu even if its empty. If you must do something regularly, then consider using co-routines, or using a master manager component.
- In your scripts, always cache transforms and components, its much cheaper than getting them during Update.
- In your scripts, never use methods like Find(), except perhaps in Start(), its very expensive.
- In your scripts, avoid using Vector3.Distance(), use Vector3.sqrMagnitude instead.
- In your scripts, avoid using Ray casting, and if you must use, then use sparingly.
- When using colliders, in order of cheapest to most expensive, use Sphere, Capsule, Cube, Mesh, Convex Mesh. Mesh colliders are way more expensive, so estimate the collision volume with a sphere or capsule if you can.
- Use GPU Instancing on your SpeedTree’s. Check this post on how to set it up!
- Set per layer cull distances on your camera using Camera.layerCullDistances
Low end settings:
Here is a sample setup for Gaia Defaults terrain settings for a low end desktop or mobile. Even if not using Gaia, these settings can be set directly in your terrain object, and will still help your frame rate.
Make a duplicate of your Gaia Defaults object and then enter these settings and save. Then open up Gaia Manager and drag it onto the Defaults setting. Gaia will then use these defaults when it creates new terrains.
Note: By dropping values, as suggested here, you are also dropping the accuracy of how Unity will place your textures and grasses, as well as dropping the visual fidelity of the end result. You will need to experiment with these to balance visual fidelity against the frame rate you get on your device. Be aware also that the mobile industry is advancing at a very rapid pace, so you need to make these decisions against where you think the hardware market will be by the time you are ready to launch.
For a more thorough exploration of game optimization in Unity I recommend having a read of an excellent book called Unity 5 Game Optimisation.
Also check these articles at Unity web site:
- Unity 3D Game Optimization And Lighting Articles
- Optimizing graphics rendering in Unity games
- Optimizing graphics performance
- Practical Guide to Optimization for Mobiles
- Graphics overview
Check out Gaia:
- Check out Gaia on the asset store.