Dicas de otimização para Unity

Unity é um mecanismo de jogo popular não apenas entre desenvolvedores independentes, mas também entre grandes empresas.

Possui uma interface amigável, um pipeline de renderização poderoso, um sistema de componentes fácil de usar e, por último, oferece suporte a uma vasta gama de plataformas.

Mas com uma interface fácil de usar, é ainda mais fácil complicar demais o seu jogo (por exemplo, colocando muitos objetos desnecessários, etc.), por isso é importante ter otimização em mente durante todo o curso do desenvolvimento.

Aqui estão dicas importantes para as 3 categorias principais (Renderização, Scripting, e Áudio) que ajudarão você a melhorar o desempenho do seu jogo:

Renderização

Dica 1: mantenha os objetos com o componente Renderer sem escala

Objetos sem escala são aqueles que possuem uma escala de (1, 1, 1). Nesse caso, Unity não precisa fazer cálculos adicionais para redimensionar o objeto em cada quadro.

Exemplo: Digamos que você tenha um modelo de casa que é muito grande ou muito pequeno para o seu nível. A coisa natural a fazer é dimensioná-lo assim:

Redimensionamento de construção

Na maioria das vezes não há problema em dimensionar o objeto na visualização da cena, mas se você planeja ter muitas instâncias duplicadas desse objeto, é preferível alterar a escala nas configurações de importação.

A primeira coisa que você precisa fazer é dimensionar o modelo até que ele atenda às suas necessidades (por exemplo, o edifício acima foi dimensionado de (1, 1, 1) a (0,49482, 0,49482, 0,49482)) e, em seguida, selecionar o modelo no Projeto visualizar e nas configurações de importação observe o fator de escala (geralmente é 1 ou 0,1).

Defina o novo valor, que deve ser igual ao fator de escala padrão multiplicado pela nova escala (no meu caso é 1 x 0,49482 = 0,49482) e clique em Aplicar. Agora volte para o modelo na vista da cena e defina a escala novamente para (1, 1, 1).

Configurações do fator de escala do Unity 3D

O objeto agora é dimensionado da maneira necessária, preservando a escala padrão (1, 1, 1).

Esta dica é especialmente importante para objetos animados que usam SkinnedMeshRenderer, pois esse componente é mais caro para renderizar e ter uma escala de (1, 1, 1) simplifica o processo de renderização.

Dica 2: Use o mínimo de luzes possível

Existem 3 tipos de luzes em Unity (luz direcional, luz pontual e holofote). Em termos de desempenho a luz direcional é a mais barata para renderização, depois a Point e por último o Spotlight.

Geralmente, você não deseja ter mais de uma luz direcional por cena e, para as luzes spot e pontuais, tente ter o mínimo possível (ou nenhuma).

Em termos de sombras em tempo real, embora melhore o aspecto visual do jogo, tem uma sobrecarga de alto desempenho, então geralmente é melhor desativá-las ou transformá-las em lightmaps e light sondas.

Dica 3: use shaders transparentes com cuidado

Use Shaders Transparentes ou de Partículas apenas em superfícies que precisam ser transparentes (ex. Cercas, Partículas de Fumaça, etc.)

Objetos com transparência requerem uma passagem de renderização adicional que pode reduzir o desempenho, especialmente em plataformas com recursos limitados, como Mobile ou Web.

Scripts

Dica 1: sempre armazene em cache as referências de componentes

Você deve sempre armazenar em cache as referências dos componentes se planeja acessá-los a cada atualização.

Por exemplo, verifique o script abaixo:

Ruim

using UnityEngine;

public class Script1 : MonoBehaviour
{
    float someValue = 0;

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

Aqui temos Script1 que obtém a variável "someValue2" do Script2 e a atribui a uma variável local.

Agora, chamar apenas um GetComponent de cada quadro não terá nenhum impacto no desempenho, porém, você deve adotar o hábito de armazenar em cache os componentes que serão usados ​​com frequência.

Há duas maneiras de armazenar em cache um componente em um script: criar uma variável pública e atribuí-la na visualização do Inspetor ou criar uma variável privada e atribuí-la em Iniciar ou Acordado. Confira o exemplo abaixo:

Bom

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;
    }
}

Muito melhor, o Script2 agora pode ser acessado a cada atualização sem sobrecarga de desempenho.

Faça o mesmo para componentes integrados, como BoxCollider, Rigidbody, etc. (exceto Transform e GameObject, que já estão armazenados em cache por padrão para que você possa acessá-los imediatamente).

Dica 2: use SendMessage com cuidado

SendMessage permite chamar uma função específica (se existir) em cada MonoBehaviour que está anexado a um objeto de jogo.

Por exemplo, quando você dispara uma arma no jogo, você pode infligir dano rapidamente quando a bala atinge o inimigo, sem a necessidade de usar GetComponent e outras coisas extras.

No entanto, esse método não deve ser chamado com muita frequência, pois exige bastante computação.

Dica 3: use GameObject.Find e GameObject.FindWithTag com cuidado

GameObject.Find, GameObject.FindWithTag e GameObject.FindGameObjectsWithTag permitem pesquisar rapidamente os objetos na cena. Esses métodos são muito mais lentos que GetComponent e só devem ser usados ​​durante a inicialização.

Dica 4: Evite usar OnGUI

Historicamente OnGUI era a única maneira de criar menus em Unity. Mas desde então, foi adicionada uma alternativa chamada UI Canvas que é muito melhor em termos de desempenho e oferece muito mais funcionalidades.

No entanto, OnGUI ainda continua sendo uma forma viável de criar UI em Unity e se você realmente precisar usá-lo, tenha em mente que OnGUI é chamado pelo menos duas vezes por quadro (duas vezes mais que Update e LateUpdate) então não faça isso. use-o para qualquer cálculo além da IU.

Uma coisa que você pode fazer é ter um script separado que contenha apenas OnGUI e ativá-lo/desativá-lo quando necessário.

Por exemplo:

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;
        }
    }
}

Tanto o UIScript quanto o Script1 estão anexados ao mesmo GameObject. Script1 controla quando mostrar o menu.

Quando o player pressiona Tab o UIScript é habilitado, mostrando os botões. Pressionar Tab novamente o desativa, ocultando os botões.

Enquanto o UIScript está desativado, o método OnGUI não é chamado, o que por sua vez melhora o desempenho.

Dica 5: use o Profiler

O Profiler é uma das ferramentas mais importantes na hora de identificar gargalos e quedas de fps, facilitando encontrar a causa exata do baixo desempenho.

Áudio

Os clipes de áudio podem ser otimizados certificando-se de que suas configurações de importação estejam corretas.

Configurações de importação de áudio ideais dependerão da duração do áudio, frequência de reprodução e plataforma de destino.

Artigos sugeridos
Como utilizar a atualização no Unity
O gerador de outdoors para Unity
Melhorando o desempenho de um jogo para celular no Unity
Configurações de importação de clipe de áudio do Unity para obter o melhor desempenho
Unity otimize seu jogo usando o Profiler
Dicas do Twitter para a Unidade
Os conceitos fundamentais do design de jogos