
Este artigo discute a otimização dos elementos da interface do usuário dos projetos feitos no Unity. Com base nas informações da documentação oficial e na experiência pessoal, tentei explicar claramente os princípios de operação dos elementos da interface do usuário. Também aqui você encontrará dicas práticas que ajudarão a melhorar o desempenho do seu projeto em relação à interface do usuário.
Terminologia
Os elementos da interface do usuário são todos os elementos do Unity relacionados à construção de uma interface do usuário. Isso inclui, por exemplo: botão, texto, imagem, menu suspenso etc.
Tela (tela) - o elemento básico da interface do usuário, que é um contêiner para o restante dos elementos.
Malha - um conjunto de parâmetros que descrevem um modelo 3D.
Um quad é uma malha que é um quadrilátero.
Lote - combinando malhas de objetos em uma malha grande para renderização mais rápida.
Draw-call - um comando para desenhar do mecanismo para a API de gráficos (por exemplo, OpenGL ou Direct3D).
Fila transparente - uma fila para renderizar objetos transparentes.
Mistura alfa ( mistura alfa) - um algoritmo para misturar pixels no canal alfa para obter uma imagem com transparência.
Atlas é um tipo de recurso que combina várias texturas em uma.
Introdutório
Na otimização da interface do usuário, não há regras universais que funcionem em qualquer situação. Tudo se resume a encontrar um equilíbrio entre o custo do lote e o número de chamadas de compra. Podem ser distinguidos quatro problemas principais:
- Carga de GPU muito alta (excesso de carga na renderização);
- muita carga de CPU ao reconstruir a tela;
- muitos elementos de mudança levando à reconstrução da tela;
- A carga da CPU é muito grande para geração de malha (geralmente associada ao texto).
Renderização da interface do usuário do Unity
O elemento básico da interface do usuário do Unity é a tela. Ele é responsável por gerar, classificar e renderizar malhas dos elementos da interface filho. Todos os elementos da interface do usuário devem ser filhos de qualquer tela, caso contrário, eles não serão exibidos no jogo.

A renderização ocorre do objeto mais distante ao mais distante da câmera (de trás para frente) na fila Transparente com mistura alfa.
Separadamente, deve-se notar que a transparência dos elementos da interface do usuário NÃO afeta o desempenho. Mesmo se o elemento consistir inteiramente em pixels "opacos", ele ainda será renderizado usando a mistura alfa.
Também é importante entender que, ao renderizar, todos os pixels de todos os elementos ativos são processados. Não depende se eles são visíveis, bloqueados por outros objetos ou mesmo completamente transparentes.
Redesenhando a interface do usuário
A reformulação da interface do usuário é um processo de várias etapas, incluindo a criação das malhas de cada elemento da interface do usuário e a tentativa de corrigir essas malhas para minimizar o número de chamadas de empate .
A reconstrução ocorre em quatro estágios:
- Estruturas de elementos são analisadas.
- As malhas de todos os elementos ativos, incluindo elementos com transparência zero, são reconstruídas.
- Reconstrói materiais para malhas de elementos de malha.
- Todos os elementos são desenhados de acordo com sua ordem.
A tela reconstruída é armazenada em cache e reutilizada até que um dos elementos na tela seja marcado como modificado.
Objetos sujos são marcados que foram ativados ou desativados; que mudaram de material, posição, escala, rotação; o valor do texto do componente de texto foi alterado; reatribuição dos pais, etc.
Nesse caso, a reconstrução da tela novamente contém pelo menos um elemento alterado. Verdade, isso se aplica apenas à tela na qual o elemento está localizado. Ou seja, alterações nos elementos nas telas filho não afetam os pais.
Quanto mais elementos na tela, maior o custo de analisar e classificar objetos.
Malha
A mesclagem de malhas, ou lotes, ajuda a reduzir a carga na GPU, reduzindo o número de chamadas de empate. Durante o processo de lote, as malhas são classificadas por profundidade e verificadas quanto à sobreposição. Ao passar do elemento distante para o elemento próximo (ou do elemento superior para o inferior na hierarquia), objetos com os mesmos materiais ou texturas são combinados em uma malha na mesma tela. Para isso, não deve haver objetos com outros materiais entre eles. Além disso, objetos com outros materiais não devem se sobrepor aos objetos cozidos com seus recipientes gerais. A operação de lote é multithread, seu desempenho varia muito, dependendo do número de núcleos no processador.
O texto pode tremular com outro texto se eles tiverem a mesma fonte. Não importa se as configurações e os estilos de fonte são iguais ou diferentes. Se as fontes forem diferentes, o texto não será rolado.
Também deve-se ter em mente que o texto pode sobrepor um objeto ao seu contêiner geral e essa sobreposição pode ser facilmente ignorada.

Considere um exemplo. Existem três objetos A, B e C organizados em uma hierarquia desta maneira:


Na imagem à esquerda, os objetos A e C serão mesclados, porque têm o mesmo material e não se cruzam com o objeto B. Na imagem à direita, os objetos A e C não serão combinados, porque existe uma interseção com o objeto B.
Dicas gerais de otimização da interface do usuário
Antes de iniciar a otimização, é altamente recomendável que você crie um perfil da interface do usuário. Isso ajudará a identificar gargalos que levam à perda de desempenho (se houver). Para criação de perfil, existem muitas ferramentas, integradas ao Unity (Unity Profiler) e de terceiros. Mas não discutiremos os problemas de criação de perfil de interface do usuário neste artigo.
Aqui estão algumas sugestões para otimizar sua interface do usuário no Unity:
- Desligue objetos invisíveis. Se um elemento for sobreposto por um elemento opaco, será necessário desativar o GameObject ou o GameObject pai do elemento sobreposto. Ao mesmo tempo, os elementos da interface com alfa definido como 0 ainda serão desenhados. Para esses objetos, você precisa habilitar Cull Transparent Mesh no componente Canvas Renderer ou simplesmente desabilitar objetos invisíveis.

- Desative objetos do mundo ocultos por uma interface opaca. Se a interface não cobrir o mundo inteiro, você poderá salvá-la na Textura de renderização e desligar a câmera mundial.
- Minimize o número de pixels para desenhar. Combine o máximo de imagens possível em uma. Por exemplo, faz sentido transformar os botões em um único sprite, em vez de separar camadas com fundo, traçado, corpo do botão etc. Isso reduzirá a flexibilidade de trabalhar com esses elementos e poderá levar ao entupimento de recursos, portanto, é necessário buscar um compromisso.

- Evite elementos vazios que servem apenas para organizar a estrutura (não use elementos como nomes de "pastas" na hierarquia de documentos).
- Evite cruzar objetos que não possam assar um com o outro. Se possível, é melhor alterar a posição na hierarquia, o tamanho do contêiner ou a posição de elementos sobrepostos e sem empilhamento.
- Use tela separada ou aninhada para elementos dinâmicos. Portanto, você minimiza o custo de classificação e reconstrução da estrutura da tela que contém um grande número de elementos. Telas aninhadas são mais convenientes, porque herdar as configurações da tela pai. Ao mesmo tempo, ao alterar a tela pai, todos os aninhados também serão reconstruídos. Isso é bastante raro, mas acontece (por exemplo, ao alterar a resolução da tela). Lembre-se de que objetos de diferentes telas ou telas aninhadas NÃO são projetados para renderização em conjunto. Recomenda-se dividir as telas de acordo com a regularidade da atualização dos elementos. Os elementos estáticos precisam ser colocados em uma tela separada e serão desenhados apenas uma vez. Se houver elementos que mudam constantemente, é melhor combiná-los em outra tela, porque eles ainda irão se reconstruir. A alteração de objetos também pode ser dividida em várias telas pela taxa de atualização. Por exemplo, os elementos atualizados a cada quadro são colocados em uma tela e os elementos atualizados com menos frequência em outro.
- Desativar o Pixel Perfect aumentará significativamente a produtividade. Isso é especialmente verdadeiro para objetos atualizados constantemente com um grande número de elementos (por exemplo, rolagem de inventário).

A tela

Tela aninhada - Se você precisar desativar a tela, não desative o objeto que a contém (através da função SetActive ). Na próxima vez em que você ativar os elementos dessa tela, serão marcados como alterados e todos os elementos serão reconstruídos. É melhor desativar o componente da tela em si, para que toda a estrutura e os dados armazenados não sejam alterados. Na próxima vez em que você ativar o componente da tela, eles apenas iniciarão o desenho.

- Para telas com Screen Space - Camera ou World Space selecionado no parâmetro Render Mode , sempre instale uma câmera. Se você não instalá-lo, o sistema de interface com o usuário em cada quadro o pesquisará através de Object.FindObjectWithTag para localizar Camera.main , e isso afetará o desempenho.
- Para não criar várias texturas idênticas, você pode criar uma em tons de cinza e pintá-la através do componente Imagem, selecionando a cor desejada.

- Deixe o sinalizador Raycast Target apenas nos itens que precisam de eventos de entrada e remova o restante. Por padrão, o destino Raycast está ativado em muitos elementos (imagem, texto etc.). Isso complica e retarda o trabalho do componente Raycaster , que lida com eventos de entrada na interface do usuário do Unity. Ao clicar ou gravar, ele percorre toda a hierarquia de elementos e pesquisa todos os componentes gráficos com o sinalizador Raycast Target definido, verifica-os quanto à possibilidade de eventos de entrada e, após passar com êxito nas verificações, os adiciona à lista de ocorrências. Depois disso, a lista de ocorrências é classificada por profundidade, objetos fora da tela são descartados. Como resultado, fornece a lista final de ocorrências.
Na interface do usuário do Unity, muitos componentes (imagem, texto etc.) têm o sinalizador Raycast Target .

Alvo Raycast no componente de imagem.

O TextMeshPro o oculta na guia Configurações extras .

No TextMeshPro, você pode definir a configuração padrão do Raycast Target em Configurações do Player -> TextMest Pro -> Configurações -> Ativar o Raycast Target .
Os elementos são testados quando:
- Alvo Raycast ativado
- o próprio elemento é ativado e ativado;
- o ponto de inserção está na interseção com RectTransform.
Também faz sentido remover o sinalizador Raycast Target dos filhos se o objeto raiz já o possuir e sobrepor completamente os filhos com sua geometria. Por exemplo, o botão padrão da interface do usuário do Unity.

A imagem sobrepõe completamente o texto à sua forma; nesse caso, você pode remover o Raycast Target do componente de texto.

Se todos os elementos da tela não aguardarem eventos de entrada, você poderá remover o componente Graphic Raycaster da tela / tela aninhada.
Trabalhar com texto e sua otimização
O texto na interface do Unity consiste em grades nas quais cada símbolo é criado em seu próprio quad. A malha é reconstruída sempre que o valor do texto é alterado. A reconstrução também ocorre se o componente de texto ou seu pai tiver sido desativado e ativado novamente.
Por padrão, as fontes no Unity são adicionadas como dinâmicas. Para cada fonte dinâmica usada no componente de texto no palco, um atlas é criado. Somente caracteres usados estão incluídos neste atlas. Por exemplo, se o campo de texto contiver o texto "Novo texto", o atlas criado para ele conterá os caracteres "N", "e", "w", "T", "x" e "t". Para cada caractere diferente em tamanho ou estilo, uma representação será criada no atlas.

Texto no palco

Atlas de fontes
Se durante a execução do programa o conteúdo do componente de texto for alterado e aparecerem caracteres que não estão no atlas, o atlas inteiro será reconstruído. Se houver espaço livre na textura do atlas, os símbolos necessários serão simplesmente adicionados lá. Ao mesmo tempo, os caracteres que não estão em uso no momento não serão excluídos. Se não houver espaço suficiente no atlas para novos caracteres, seu tamanho será dobrado e preenchido novamente com base nos caracteres usados nos componentes de texto ativos.
Se um número estritamente definido de caracteres for definido para um projeto, por exemplo, apenas o alfabeto latino, vale a pena usar fontes estáticas armazenadas permanentemente na memória. Se o seu projeto permitir um grande número de caracteres, é melhor recorrer a fontes dinâmicas.
Você também pode melhorar o desempenho substituindo o componente de texto por um sprite. Por exemplo, os números que aparecem no jogo (pontuação) podem ser feitos usando sprites de um atlas contendo um conjunto apenas dos caracteres necessários. Nesse caso, não haverá custos para reconstruir a tela e o atlas da fonte.
O uso de fontes substitutas, listadas no campo Nomes das fontes nas configurações de fonte, aumenta a memória usada. Isso é especialmente perceptível nas fontes pictográficas.
Não é recomendado usar o Best Fit , pois essa opção leva a um transbordamento rápido do atlas e causa sua reestruturação.
Melhor ajuste ignora as configurações de tamanho da fonte e tenta ajustar o texto no retângulo de contorno do componente de texto.
Agora, algumas palavras sobre o TextMeshPro (TMP) , um substituto popular para o componente de texto padrão do Unity. O TextMeshPro também reconstrói sua grade sempre que o valor do texto é alterado. No entanto, o texto TMP não usa fontes dinâmicas. Para isso, atlas de fontes são gerados antecipadamente, que incluem todos os caracteres necessários. Se, para algum texto em cena, não houver nenhum caractere na fonte atribuída ao seu componente, o TMP começará a procurar nas fontes de backup. Se não houver nada lá, o TMP tentará encontrar esse caractere em todas as fontes baixadas.
O Melhor ajuste no TMP não cria problemas como um componente de texto comum, portanto pode ser usado.
Um grande número de fontes com diferentes localizações ou grandes atlas de fontes pode consumir muita memória. Portanto, é melhor usar o pré-carregamento apenas das fontes necessárias para uma localização específica.
A World Space recomenda o uso do TextMeshPro em vez do TextMeshProUGUI .
TextMeshProUGUI é usado em telas.
Atlas de Sprite
Os atlas de Sprite são um tipo de recurso que combina várias texturas em uma. Eles permitem reduzir o número de draws ( draw calls ) e aumentar a produtividade.
Criação de Atlas.
Crie um atlas: Ativo> Criar> Atlas Sprite .
Selecione o atlas e coloque o sprite necessário em Objetos para embalagem .

Clique em Visualizar pacote para visualizar o atlas.

Lembre-se de que, mesmo que um ou mais sprites do atlas sejam usados em cena, o atlas ainda será carregado por inteiro. Portanto, não faz sentido tentar incorporar todas as imagens em um atlas gigante, que em dispositivos com pouca RAM pode ter sua parte tangível. É melhor invadir alguns atlas menores, por exemplo, o atlas da interface do usuário do menu do jogo e o atlas da interface do modo de jogo.
Evite grandes espaços vazios no atlas, para não ocupar espaço em excesso de memória. Para fazer isso, você pode redimensionar o atlas ou adicionar imagens adicionais para preencher o espaço vazio o máximo possível.


Para imagens que não estão no atlas, você deve selecionar as configurações corretas.
Para cada formato de compactação, existem requisitos sob os quais ele funcionará com eficiência. Os requisitos de imagem mais comuns:
- largura e altura devem ser múltiplos da potência de dois;
- largura e altura devem ser múltiplos de 4;
- ambos os parágrafos anteriores juntos.
Caso contrário, a textura não será compactada e ocorrerão custos adicionais de memória do dispositivo.
Ao escolher um formato de compactação, o Unity informará se algum requisito para o formato selecionado não foi atendido.

Também não esqueça que os formatos são diferentes do dispositivo de destino. Para mais informações e recomendações sobre formatos, consulte a documentação oficial :
Resuma métodos de otimização
- Desativar objetos invisíveis e transparentes
- Minimize o número de itens
- Evite cruzar objetos que não possam assar uns com os outros
- Distribuir itens por tela com base na taxa de atualização
- Para telas com elementos atualizados com freqüência, desative o Pixel Perfect
- Desativar destino de Raycast em itens não clicáveis
- Remova o componente Graphic Raycaster na tela em que todos os elementos não podem ser clicados
- Para componentes de texto normais, não use Melhor ajuste
- Para TextMeshPro No World Space, use TextMeshPro em vez de TextMeshProUGUI. TextMeshProUGUI usado em telas
- Defina a câmera nas configurações de tela, se necessário
- Desative a tela desabilitando o componente de tela
- Use sprites em escala de cinza e colore as configurações do componente de imagem
- Use atlas de sprite
- Use o tamanho e formato de compactação corretos para texturas que não estão no atlas
Fontes:
Informações abrangentes sobre otimização da interface do usuário do Unity
Outra fonte oficial sobre este tópico contém informações de forma mais concisa.
Artigos técnicos da Atlas
Informações adicionais sobre formatos de compactação de textura