Unity 3D Optimization Tips

Unity 3D Optimization Tips

by NSDG • Jul 7, 2018 • 0 Comments
1075

Unity 3D is a game engine that is popular not only among indie developers but the big companies aswell.

It features a user-friendly interface, a powerful Rendering pipeline, an easy-to-use component system and lastly, it supports a wast array of platforms.

But with an easy-to-use interface it's even easier to overcomplicate your game (for example by placing alot of unncesessary objects etc.), so it's important to have optimization in mind during the whole course of development.

So here are an important tips for 3 major categories (Rendering, Scripting and Audio) that will help you improve the performance of your game:

Rendering

Tip 1: Keep the Objects with Renderer component unscaled

Unscaled objects are those that have a scale of (1, 1, 1). In that case Unity does not have to do additional calculatinons to rescale the object each frame.

Example: Let's say you have a model of a house which is too big or too small for your level. The natural thing to do is to scale it like this:

Building resize

Most of the time it's Ok to scale the object in Scene view, but if you plan to have alot of duplicated instances of that object it's preferable to change the scale in the Import Settings.

First thing you need to do is to scale the model until it fits your needs (ex. the building above was scaled from (1, 1, 1) to (0.49482, 0.49482, 0.49482)), then select the model in the Project view and in the Import Settings note the Scale Factor (Usually it's either 1 or 0.1).

Set the new value, which should equal default scale factor x the new scale (In my case it's 1 x 0.49482 = 0.49482), then hit Apply. Now go back to the model in the scene view and set the scale back to (1, 1, 1).

Unity 3D Scale factor settings

The object is now scaled the way you need while preserving the default (1, 1, 1) scale.

This tip is especially important for animated objects that use SkinnedMeshRenderer as this component is more expensive to render, and having a scale of (1, 1, 1) simplifies the rendering process.

Tip 2: Use as few lights as possible

There are 3 types of lights in Unity (Directional light, Point light and Spot light). In terms of performance Directional light is the cheapest for rendering, then Point and lastly the Spot light.

Generally you don't want to have more than 1 directional light per scene and for a spot and point lights try to have as few as possible (or none at all).

In terms of realtime shadows, while it enhances the visual aspect of the game, it has a high performance overhead so generally it is better to disable them or bake them into lightmaps and light probes.

You can read more about Lightmapping by clicking here and to learn more about Light Probes click here.

Tip 3: Use Transparent Shaders with caution

Only use Transparent or Particle Shaders on surfaces that need to be transparent (ex. Fences, Smoke Particles etc.)

Objects with transparency require an additional rendering pass which could reduce performance, especially on platforms with limited resources, such as Mobile or Web.

Scripting

Tip 1: Always cache component references

You should always cache component references if you plan to access them every Update.

For example check the script below:

Bad

using UnityEngine;

public class Script1 : MonoBehaviour
{
    float someValue = 0;

    // Update is called once per frame
    void Update()
    {
        someValue = GetComponent<Script2>().someValue2;
    }
}

Here we have Script1 which get's the variable "someValue2" from Script2 and assigns it to a local variable.

Now, calling just one GetComponent each frame won't have any impact on performance, however you should adopt a habit of caching the components that will be used frequently.

There is two ways you can cache a component, either create a public variable and assign it in Inspector view, or create a private variable and assign it from Start or Awake. Check the example below:

Good

using UnityEngine;

public class Script1 : MonoBehaviour
{

    float someValue = 0;

    Script2 script2Cached;

    // Use this for initialization
    void Start()
    {
        script2Cached = GetComponent<Script2>();
    }

    // Update is called once per frame
    void Update()
    {
        someValue = script2Cached.someValue2;
    }
}

Much better, Script2 can now be accessed each Update without performance overhead.

Do the same for built-in components, such as BoxCollider, Rigidbody etc. (except Transform and GameObject, those are already cached by default so you can access them right away).

Tip 2: Use SendMessage with caution

SendMessage let's you call a specific function (if exist) on every MonoBehaviour that is attached to a game object.

For example when you shoot a weapon in the game you can quickly inflict damage when the bullet hits the enemy, without the need to use GetComponent and other extra stuff.

However this method should not be called too frequently as it's really slow.

Tip 3: Use GameObject.Find and GameObject.FindWithTag with caution

GameObject.Find, GameObject.FindWithTag and GameObject.FindGameObjectsWithTag let you quickly search for the objects in the Scene. These methods are much slower than GetComponent and should only be used during initialization.

Tip 4: Avoid using OnGUI

Historically OnGUI was the only way to make menus in Unity 3D. But since then, an alternative was added called UI Canvas which is much better in terms of performance, but may be a bit tedious to setup.

Nevertheless OnGUI still remains a viable way to make UI in Unity and if you absolutely need to use it, keep in mind that OnGUI is called atleast twice per frame (twice as much as Update and LateUpdate) so do not use it for any calculations besides UI.

One thing you can do, is to have a separated script that only has OnGUI in it and enable/disable it when needed.

For example:

UIScript.cs

using UnityEngine;

public class UIScript : MonoBehaviour {

    void OnGUI()
    {
        if(GUI.Button(new Rect(5, 5, 125, 25), "Button 1"))
        {
            //Button 1 was pressed, Do Something
        }
        if (GUI.Button(new Rect(140, 5, 125, 25), "Button 2"))
        {
            //Button 2 was pressed, Do Something
        }
    }
}

Script1.cs

using UnityEngine;

public class Script1 : MonoBehaviour
{

    UIScript uiScript;

    // Use this for initialization
    void Start()
    {
        uiScript = GetComponent<UIScript>();
        uiScript.enabled = false;
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Tab))
        {
            //toggle UIScript when Tab is pressed
            uiScript.enabled = !uiScript.enabled;
        }
    }
}

Both UIScript and Script1 are attached to the same GameObject. Script1 controls when to show the menu.

When the player press Tab the UIScript is enabled, showing the buttons. Pressing Tab again deactivates it, hiding the buttons.

While UIScript is deactivated, OnGUI method is not called which in turn improves performance.

Audio

For the Audio optimization please check the article below:

Unity 3D Audio Clip Import Settings For The Best Performance

Hope you found these tips useful. Feel free to share this post on social media!