General Performance Tips
From Unify Community Wiki
Revision as of 09:27, 21 June 2006 by NCarter
The following tips are not meant to be absolutes but rather guidelines for Unity users that want to learn how to make a well performing game.
Official Tips from OverTheEdge
- Do not have needless meshes. If you have a character in your game, it should probably be only 1 mesh. If you have a ship, it should probably only be one mesh. There is a significant overhead for each mesh Unity renders due to realities of hardware and drivers.
- One material per mesh. Each material that is rendered is treated like a separate mesh getting rendered.
- The performance gained from using lower polygon meshes under 500 polygons is minimal if at all there. The majority of graphics cards have hardware transform and lighting, which means they can process ridiculous amounts of polygons per second. Additionally there is overhead for submitting a mesh to the graphics card to render, so being really thrifty on polygons is probably only making your game look blocky.
- Starting out, make characters with about 1500-2000 triangles. This number can vary wildly, but for starting artists this should provide a good compromise between quality and performance for one level of detail. Note that if your model has quadrilaterals, (quads) Unity will convert each quad into two triangles on import.
- Each pixel light rendered is effectively another rendering pass. Pixel lights can make your game look great but don't go too nuts with them. However, using the Quality Manager to adjust the number of pixel lights rendered for each quality level is a great way to provide ample performance/quality tradeoffs in your built game.
- Spot lights are more expensive than point lights which are more expensive than directional lights. A good way to light a scene is to first get the effect you want correct. Then look at all the lights you have and see which ones are important and see if you can reduce the lights while keeping the effect similar enough.
- Point and spot lights only affect meshes within their range. If a mesh is out of range of a point or spotlight and the light is set to attenuate, the mesh will not be affected by the light thus saving performance. This way one could theoretically have many small point lights and still have good performance because they only affect a few objects. Remember, though, a mesh will only respond to the eight brightest lights affecting that mesh.
- Keep your FixedUpdate functions as lean as possible. These functions can get called around 50-100 times a second per applicable script per object, so they are a good target to optimize. If there are things that need to be only done when the display is updated, put that code inside the Update function.
- If possible, disable scripts on objects when they are not needed. If you have a large level in your game and there is an enemy kilometers away, you can probably switch off its AI script until the camera gets closer. A good way to switch on and off objects is by using gameObject.SetActiveRecursively(false) and sphere or box colliders set as triggers.
- Beware the empty Update functions. When creating new scripts with the Assets menu, they include an empty Update() function. Remove it if you don't need it as it comes at a (rather light) performance cost. This performance cost applies to all overridable functions in MonoBehaviour scripts, with Update and FixedUpdate being the main targets.
- Refer to a GameObject by the most logical component. One could theoretically write: someGameObject.transform.gameObject.rigidbody.transform.gameObject.rigidbody.transform, but there is a whole lot of needless work done there. If you want to deal with an object's Transform, refer to it in your script as such in the first place.
- Coroutines are your friends. Coroutines have only a tiny overhead and can be preferrable to an Update method that is called all the time needlessly. For example, if you had a script to fade in and out a light on command, you could do the fading in a coroutine instead of in Update. That way, most of the time when the light is not fading, the script takes a minimum amount of performance. If the fading was done in the Update function, you would be inefficiently polling to see if there is fading to be done.
- Don't use methods which search for objects any more than is necessary. This includes methods such as GameObject.FindByTag() and GameObject.GetComponent(). These methods are optimised to operate as quickly as possible, but they still have to search through all the relevant objects to find the one you want. The most important thing is to avoid calling search methods repeatedly in Update() or FixedUpdate(). Instead, call the method once, store its result in a member variable of your class, and then use the member variable to access it the next time you need it.
- More complicated looking shaders probably cost performance compared to simpler ones. The VertexLit Diffuse shader should be the fastest shader that still takes a texture and responds to lighting. However, if there are no pixel lights in the scene or if all pixel lights are off in the Quality Manager, most shaders will fall back to a more simple vertex version.
- Keep the size of textures as small as possible while still looking nice. If your graphics card does not have enough memory to keep all the textures in memory at once, they will get placed into regular system memory and uploaded to the card during rendering. This can be okay as there is a lot of bandwidth available on newer computers; however, if you go too nuts your game will completely choke on computers with low amounts of graphics memory. There is no need to edit the size of textures in your image editor. You can edit the size that Unity imports the image on in each image's Settings.
- Each rigidbody takes computation, so less are of course ideal. Rigidbodies also have the ability to sleep when their angular and translational velocity drop below a certain threshold. When this happens, the amount of computation they require drops significantly and remains low until they have a force manually applied to them or a collider touches their collider if it exists.
- Complicated collisions take more computation than simpler ones. A large pile of rigidbodies with sphere colliders should be more complicated to process than if those spheres were laying far from each other on a terrain.