O que você está procurando?
Hero background image
Programação avançada e arquitetura de código
Explore a arquitetura do seu código para otimizar ainda mais a renderização de seus gráficos. Este é o quarto de uma série de artigos que descompacta dicas de otimização para seus projetos de Unity. Use-os como um guia para rodar em taxas de quadros mais altas com menos recursos. Depois de experimentar essas melhores práticas, não deixe de conferir as outras páginas da série: Configurando seu projeto Unity para um desempenho mais forte Otimização de desempenho para gráficos de alta qualidade Gerenciamento do uso da GPU para jogos de PC e console Desempenho de física aprimorado para um gameplay suave
Esta página foi traduzida por máquina. Para ver a versão original a fim de obter uma fonte confiável e precisa,
Entender o Unity PlayerLoop

A Unity PlayerLoop contém funções para interagir com o núcleo do motor do jogo. Esta estrutura inclui uma série de sistemas que lidam com inicialização e atualizações por quadro. Todos os seus scripts dependerão deste PlayerLoop para criar a jogabilidade. Ao fazer o perfil, você verá o código do usuário do seu projeto sob o PlayerLoop – com Editor componentes sob o EditorLoop.

É importante entender a ordem de execução do FrameLoop do Unity. Todo script Unity executa várias funções de evento em uma ordem predefinida. Aprenda a diferença entre Awake, Start, Update e outras funções que criam o ciclo de vida de um script para fortalecer o desempenho.

Alguns exemplos incluem usar FixedUpdate em vez de Update ao lidar com um Rigidbody ou usar Awake em vez de Start para inicializar variáveis ou o estado do jogo antes do início do jogo. Use estes para minimizar o código que é executado em cada quadro. Acordar é chamado apenas uma vez durante a vida útil da instância do script e sempre antes das funções de Início. Isso significa que você deve usar Start para lidar com objetos que você sabe que podem se comunicar com outros objetos ou consultá-los conforme foram inicializados.

Veja o fluxograma do ciclo de vida do script para a ordem específica de execução das funções de evento.

Diagrama do gerente de atualizações personalizado
Crie um Gerenciador de Atualizações personalizado

Se o seu projeto tiver requisitos de desempenho exigentes (por exemplo, um jogo de mundo aberto), considere criar um Gerenciador de Atualizações personalizado usando Atualizar, AtualizaçãoTardia ou AtualizaçãoFixa.

Um padrão de uso comum para Update ou LateUpdate é executar a lógica apenas quando alguma condição é atendida. Isso pode levar a uma série de callbacks por quadro que efetivamente não executam nenhum código, exceto para verificar esta condição.

Sempre que a Unity chama um método de mensagem como Update ou LateUpdate, ela faz uma chamada de interop – ou seja, uma chamada do lado C/C++ para o lado gerenciado C#. Para um pequeno número de objetos, isso não é um problema. Quando você tem milhares de objetos, essa sobrecarga começa a se tornar significativa.

Inscreva objetos ativos neste Gerenciador de Atualizações quando eles precisarem de callbacks e desinscreva quando não precisarem. Este padrão pode reduzir muitas das chamadas de interop para seus Monobehaviour objetos.

Consulte as técnicas de otimização específicas de motores de jogo para exemplos de implementação.

Minimize o código que é executado a cada quadro

Considere se o código deve ser executado a cada quadro. Você pode mover a lógica desnecessária para fora de Update, LateUpdate e FixedUpdate. Essas funções de evento do Unity são lugares convenientes para colocar código que deve ser atualizado a cada quadro, mas você pode extrair qualquer lógica que não precise ser atualizada com essa frequência.

Execute lógica apenas quando as coisas mudam. Lembre-se de aproveitar técnicas como o padrão observador na forma de eventos para acionar uma assinatura de função específica.

Se você precisar usar Atualizar, pode executar o código a cada n quadros. Esta é uma maneira de aplicar Fatiamento de Tempo, uma técnica comum de distribuir uma carga de trabalho pesada entre múltiplos quadros.

Neste exemplo, executamos a ExampleExpensiveFunction uma vez a cada três quadros.

O truque é intercalar isso com outro trabalho que roda nos outros quadros. Neste exemplo, você poderia "agendar" outras funções caras quando Time.frameCount % intervalo == 1 ou Time.frameCount % intervalo == 2.

Alternativamente, use uma classe de Gerenciador de Atualizações personalizada para atualizar os objetos inscritos a cada n quadros.

Armazene em cache os resultados de funções caras

Nas versões do Unity anterior a 2020.2, GameObject.Find, GameObject.GetComponent, e Camera.main podem ser caros, então é melhor evitar chamá-los em métodos Update.

Além disso, tente evitar colocar métodos caros em OnEnable e OnDisable se forem chamados com frequência. Chamar esses métodos com frequência pode contribuir para picos de CPU.

Sempre que possível, execute funções caras, como MonoBehaviour.Awake e MonoBehaviour.Start durante a fase de inicialização. Armazene as referências necessárias e reutilize-as mais tarde. Confira nossa seção anterior sobre o Unity PlayerLoop para a execução da ordem do script em mais detalhes.

Aqui está um exemplo que demonstra o uso ineficiente de uma chamada repetida GetComponent:

void Update()
{
Renderizador myRenderer = GetComponent<Renderizador>();
ExemploFunção(meuRenderizador);
}

Em vez disso, invoque GetComponent apenas uma vez, pois o resultado da função é armazenado em cache. O resultado em cache pode ser reutilizado na Atualização sem chamadas adicionais para GetComponent.

Leia mais sobre a Ordem de execução para funções de evento.

Evite eventos de Unity vazios e declarações de log de depuração

Declarações de log (especialmente em Update, LateUpdate ou FixedUpdate) podem prejudicar o desempenho, então desative suas declarações de log antes de fazer uma compilação. Para fazer isso rapidamente, considere fazer um Atributo Condicional junto com uma diretiva de pré-processamento.

Por exemplo, você pode querer criar uma classe personalizada como mostrado abaixo.

Gere sua mensagem de log com sua classe personalizada. Se você desativar o ENABLE_LOG pré-processador nas Configurações do Jogador > Símbolos de Definição de Script, todas as suas declarações de log desaparecem de uma só vez.

Lidar com strings e texto é uma fonte comum de problemas de desempenho em projetos Unity. É por isso que remover declarações de log e sua formatação de string cara pode potencialmente ser uma grande vitória de desempenho.

Da mesma forma, MonoBehaviours vazios requerem recursos, então você deve remover métodos Update ou LateUpdate em branco. Use diretivas de pré-processador se você estiver empregando esses métodos para teste:

#if Unity
void Update()
{
}
#endif

Aqui, você pode usar a Atualização no Editor para testar sem sobrecarga desnecessária se infiltrando na sua compilação.

Este post de blog sobre 10.000 chamadas de atualização explica como Unity executa o Monobehaviour.Update.

Desativar o registro de Stack Trace

Use as Stack Trace opções nas Configurações do Jogador para controlar que tipo de mensagens de log aparecem. Se o seu aplicativo estiver registrando erros ou mensagens de aviso na sua versão de lançamento (por exemplo, para gerar relatórios de falhas em produção), desative as Stack Traces para melhorar o desempenho.

Saiba mais sobre Registro de Stack Trace.

Use valores hash em vez de parâmetros de string

A unidade não usa nomes de string para endereçar Animator, Material ou Shader propriedades internamente. Para velocidade, todos os nomes de propriedades são hashados em Identificação da Propriedades, e esses IDs são usados para endereçar as propriedades.

Ao usar um Set ou Get método em um Animator, Material ou Shader, aproveite o método de valor inteiro em vez dos métodos de valor string. Os métodos que têm valor de string realizam a hash de string e, em seguida, encaminham o ID hash para os métodos que têm valor inteiro.

Use Animator.StringToHash para nomes de propriedades do Animator e Shader.PropertyToID para nomes de propriedades de Material e Shader.

Relacionado está a escolha da estrutura de dados, que impacta o desempenho à medida que você itera milhares de vezes por quadro. Siga o guia MSDN sobre estruturas de dados em C# como um guia geral para escolher a estrutura certa.

Interface de script de Pool de Objetos
Agrupe seus objetos

Instanciar e Destruir podem gerar picos de coleta de lixo (GC). Este é geralmente um processo lento, então, em vez de instanciar e destruir regularmente GameObjects (por exemplo, disparar balas de uma arma), use pools de objetos pré-alocados que podem ser reutilizados e reciclados.

Crie as instâncias reutilizáveis em um ponto do jogo, como durante uma tela de menu ou uma tela de carregamento, quando um pico de CPU é menos perceptível. Acompanhe este "pool" de objetos com uma coleção. Durante o jogo, basta ativar a próxima instância disponível quando necessário e desativar os objetos em vez de destruí-los, antes de devolvê-los ao pool. Isso reduz o número de alocações gerenciadas em seu projeto e pode prevenir problemas de GC.

Da mesma forma, evite adicionar componentes em tempo de execução; Invocando AddComponent vem com algum custo. A unidade deve verificar duplicatas ou outros componentes necessários sempre que adicionar componentes em tempo de execução. Instanciando um Prefab com os componentes desejados já configurados é mais performático, então use isso em combinação com seu Object Pool.

Relacionado, ao mover Transformações, use Transform.SetPositionAndRotation para atualizar tanto a posição quanto a rotação de uma só vez. Isso evita a sobrecarga de modificar um Transform duas vezes.

Se você precisar instanciar um GameObject em tempo de execução, parentar e reposicionar para otimização, veja abaixo.

Para mais informações sobre Object.Instantiate, veja a Scripting API.

Aprenda como criar um sistema simples de Pooling de Objetos no Unity aqui.

Pool de ScriptableObjects
Aproveite o poder dos ScriptableObjects

Armazene valores ou configurações imutáveis em um ScriptableObject em vez de um MonoBehaviour. O ScriptableObject é um ativo que vive dentro do projeto. Ele só precisa ser configurado uma vez e não pode ser anexado diretamente a um GameObject.

Crie campos no ScriptableObject para armazenar seus valores ou configurações, e então faça referência ao ScriptableObject em seus MonoBehaviours. Usar campos do ScriptableObject pode evitar a duplicação desnecessária de dados toda vez que você instanciar um objeto com esse MonoBehaviour.

Assista a este tutorial de Introdução a ScriptableObjects e encontre a documentação relevante aqui.

arte chave de unidade 21 11
Baixe o e-book gratuito

Um de nossos guias mais abrangentes de todos os tempos reúne mais de 80 dicas práticas sobre como otimizar seus jogos para PC e console. Criado por nossos engenheiros especialistas em Sucesso e Accelerate Solutions, essas dicas detalhadas ajudarão você a aproveitar ao máximo o Unity e melhorar o desempenho do seu jogo.

Você gostou deste conteúdo?