O que você está procurando?

Dicas e armadilhas do Unity Asset Bundles

ATTILIO CAROTENUTO / UNITYTechnical Lead
Apr 16, 2024|8 Min
Dicas e armadilhas do Unity Asset Bundles
Esta página da Web foi automaticamente traduzida para sua conveniência. Não podemos garantir a precisão ou a confiabilidade do conteúdo traduzido. Se tiver dúvidas sobre a precisão do conteúdo traduzido, consulte a versão oficial em inglês da página da Web.

Pacotes de ativos são arquivos compactados contendo ativos para seu jogo. Eles são usados para dividir seu jogo em blocos lógicos, permitindo que você entregue e atualize conteúdo sob demanda, ao mesmo tempo em que torna a compilação do seu jogo menor. Eles também são comumente usados para fornecer patches e DLCs para seu jogo. Os pacotes de ativos podem conter todos os tipos de ativos, como prefabs, materiais, texturas, clipes de áudio, cenas e muito mais, mas não podem incluir scripts.

Anteriormente, era necessário criar pacotes de ativos manualmente, marcando cada ativo adequadamente e, em seguida, rastreando e resolvendo dependências por conta própria no tempo de execução. Hoje em dia, tudo isso é cuidado pelo sistema Addressables , que criará Asset Bundles para você com base nos Asset Groups que você definir, além de carregar e manipular dependências de forma transparente.

Embora existam muitos guias sobre como os Asset Bundles funcionam, gostaria de abordar alguns aspectos menos conhecidos do sistema, com foco no desempenho do jogo, no uso do tempo de execução da memória e na compatibilidade geral.

Carregamento e descarregamento de ativos

Sempre que você tenta usar um ativo contido em um pacote, o Unity garante que o pacote correspondente seja carregado na memória e, em seguida, carrega o ativo na memória.

Embora seja possível carregar parcialmente ativos específicos dentro de um Pacote de Ativos, o oposto não é permitido. Isso significa que, assim que um ativo dentro de um pacote de ativos é carregado, ele só pode ser descarregado se todo o grupo de ativos não for mais necessário.

Como resultado, se a estrutura do seu pacote não for ideal, você frequentemente verá um aumento no uso da memória de tempo de execução conforme o jogo avança, levando à deterioração do desempenho e possíveis travamentos. Por esse motivo, é melhor evitar pacotes com uma grande quantidade de recursos, pois eles acabarão ocupando muita memória de execução e se tornarão um gargalo para o seu jogo. Em vez disso, tente empacotar os ativos com base na frequência com que eles serão carregados e usados juntos.

Compatibilidade da versão do motor

Os pacotes de ativos geralmente são compatíveis com versões futuras, portanto, pacotes criados com versões mais antigas do Unity funcionarão, na maioria dos casos, em jogos criados em versões mais recentes do Unity (supondo que você não remova as informações do TypeTree, conforme abordado mais adiante). O oposto não é verdade, então pacotes criados em uma versão do Unity mais recente do que a usada na compilação do seu jogo provavelmente não serão carregados corretamente.

À medida que a diferença de versão entre o pacote e o mecanismo usado para a compilação do jogo aumenta, a compatibilidade se torna menos provável. Também há casos em que o pacote ainda pode ser carregado, mas os objetos contidos nele não podem ser carregados corretamente na nova versão do Unity, provavelmente devido a uma alteração na maneira como os objetos são serializados, criando problemas. Nesse caso, você precisará reconstruir seus pacotes para manter a compatibilidade.

Também há um custo de desempenho no carregamento de pacotes de uma versão diferente do Unity, conforme abordado na seção TypeTree abaixo.

Por esses motivos, é recomendável testar minuciosamente sempre que atualizar a versão Unity do seu jogo em relação aos pacotes de ativos existentes e também atualizá-los sempre que possível.

Compatibilidade entre plataformas

Os pacotes de ativos geralmente não oferecem suporte multiplataforma. Enquanto estiver no Editor, você poderá carregar pacotes de outra plataforma de destino, porém no dispositivo isso não funcionará.

Isso ainda é verdade para pacotes que contêm ativos que não são necessariamente específicos da plataforma.

O motivo dessa limitação é que os dados podem ser otimizados ou compactados de maneiras que funcionam apenas para a plataforma de destino. Além disso, os pacotes podem conter dados específicos da plataforma que não devem ser compartilhados entre diferentes plataformas, o que evita o vazamento de conteúdo que não se destina a outra plataforma.

Carregando cache

O cache de carregamento é um conjunto compartilhado de páginas onde o Unity armazena dados acessados ​​recentemente para seus pacotes de ativos. Isso é global, então é compartilhado entre todos os Pacotes de Recursos do seu jogo.

Isso foi introduzido recentemente, acredito que no Unity 2021.3, e depois retroportado para 2019.4. Antes disso, o Unity dependia de caches separados para cada Asset Bundle, o que resultava em um uso de memória de tempo de execução significativamente maior (abordado abaixo em “Buffers de arquivo serializados”).

Por padrão, isso é definido como 1 MB, mas pode ser alterado definindo AssetBundle.memoryBudgetKB.

O tamanho padrão do cache deve ser suficiente na maioria dos casos, embora haja alguns cenários em que alterá-lo pode trazer benefícios ao seu jogo. Por exemplo, se você tiver pacotes com muitos objetos pequenos contidos, aumentar o tamanho do cache pode levar a mais acertos de cache, melhorando o desempenho do seu jogo.

Dados internos adicionais

Junto com os ativos do seu jogo, os Pacotes de Ativos incluem um monte de informações e cabeçalhos extras, usados ​​pelo Unity para saber quais ativos carregar e como, bem como um cache dedicado (dependendo da versão do Unity que você está usando).

Índice

Um mapa dos ativos em um pacote. É o que permite que você pesquise e carregue cada ativo individual no pacote por nome. Seu tamanho na memória normalmente não é uma preocupação, a menos que você tenha pacotes de ativos excepcionalmente grandes contendo milhares de objetos.

Tabela de pré-carregamento

A Tabela de pré-carregamento lista as dependências de cada ativo contido no seu pacote. Ele é usado pelo Unity para carregar e construir ativos corretamente.

Isso pode se tornar muito grande se os ativos contidos no seu pacote tiverem muitas dependências explícitas e implícitas, bem como dependências em cascata provenientes de outros pacotes. Por esse motivo (e muitos outros), é uma boa ideia projetar seus pacotes para minimizar a cadeia de dependência.

TipoÁrvores

TypeTrees definem o layout serializado dos objetos contidos nos Asset Bundles.

O tamanho deles depende de quantos tipos diferentes de objetos estão contidos no pacote. Por esse motivo, é uma boa ideia evitar grandes pacotes onde objetos de muitos tipos diferentes são misturados.

TypeTrees são necessárias para manter a compatibilidade ao atualizar a versão Unity da sua compilação de jogo enquanto ainda tenta carregar Asset Bundles compilados em versões mais antigas do mecanismo. Por exemplo, se o formato ou a estrutura do objeto tiver mudado, eles permitem que você faça uma leitura binária segura para que o Unity possa tentar carregá-lo de qualquer maneira. Isso tem um custo de desempenho, então, em geral, é recomendável atualizar os pacotes sempre que possível ao atualizar o mecanismo.

Opcionalmente, ele pode ser desabilitado definindo o sinalizador BuildAssetBundleOptions.DisableWriteTypeTree ao criar seus pacotes. Isso tornará seus pacotes e a sobrecarga de memória relacionada menores, mas também significa que você precisará reconstruir todos os seus pacotes sempre que atualizar a versão do mecanismo da sua compilação de jogo. Isso é especialmente doloroso se você depende de pacotes criados a partir de seus players para conteúdo gerado pelo usuário. Portanto, a menos que você tenha um motivo muito forte para isso, é recomendável manter o TypeTrees ativado.

Um caso em que TypeTrees normalmente podem ser desabilitados com segurança é em pacotes incluídos diretamente na compilação do seu jogo. Nesse caso, atualizar o mecanismo exigiria criar uma nova versão do jogo e novos pacotes de recursos, então seu aspecto de retrocompatibilidade não é relevante.

Cada pacote tem seus próprios TypeTrees, portanto, ter vários pacotes pequenos contendo o mesmo tipo de objeto aumentará ligeiramente o tamanho total no disco. Por outro lado, quando carregados, os TypeTrees são armazenados em um cache global na memória, então você não incorrerá em um custo maior de memória de tempo de execução se vários pacotes de ativos estiverem armazenando o mesmo tipo de objeto.

Buffers de arquivo serializados

Observação: Desde o Unity 2019.4, isso foi substituído por um cache de carregamento global e compartilhado, conforme descrito acima.

Quando um Asset Bundle é carregado, o Unity aloca buffers internos para armazenar seus arquivos serializados na memória.

Os pacotes de ativos regulares contêm um arquivo serializado, enquanto os pacotes de ativos de cenas de streaming contêm até dois arquivos para cada cena contida naquele pacote. O tamanho desses buffers depende da plataforma. No Switch, PlayStation e Windows RT, o tamanho será de 128 KB, enquanto todas as outras plataformas terão buffers de 14 KB.

Por esse motivo, é melhor evitar ter uma grande quantidade de pacotes de ativos muito pequenos, pois a memória ocupada por esses buffers pode se tornar significativa em comparação aos ativos que eles realmente fornecem.

Verificações de integridade do CRC

Um CRC (Cyclic Redundancy Check) é usado para fazer a validação da soma de verificação dos seus Asset Bundles, garantindo que o conteúdo entregue ao seu jogo seja exatamente o que você espera. Os CRCs são calculados com base no conteúdo descompactado do pacote.

Em consoles, os Asset Bundles normalmente são incluídos como parte da instalação do título no armazenamento local ou baixados como DLCs, o que torna as verificações de CRC desnecessárias. Em outras plataformas, como PC ou celular, é importante fazer verificações de CRC em pacotes baixados de uma CDN. Isso é para garantir que o arquivo não seja corrompido ou truncado, o que pode levar a falhas, e também para evitar possíveis adulterações.

As verificações de CRC são bastante caras em termos de uso de CPU, especialmente em consoles e dispositivos móveis. Por esses motivos, normalmente é um bom compromisso desabilitar as verificações de CRC em pacotes locais e em cache, habilitando-as apenas em pacotes remotos não armazenados em cache.

Redução de sobrecarga na pesquisa de ativos

Por padrão, o Unity oferece três maneiras de pesquisar ativos dentro de pacotes:

  • Caminho relativo do projeto (Ativos/Prefabs/Personagens/Hero.prefab)
  • Nome do arquivo de ativo (Herói)
  • Nome do arquivo de ativo com extensão (Hero.prefab)

Embora isso seja conveniente, tem um custo. Para dar suporte aos dois últimos métodos, o Unity precisa criar tabelas de pesquisa, o que pode consumir uma quantidade significativa de memória para pacotes grandes.

Além disso, carregar ativos usando um método diferente do Caminho Relativo do Projeto incorrerá em um custo de desempenho, novamente devido à consulta de tabela necessária.

Por essas razões, é recomendável evitar usar esses métodos. Você pode até desabilitá-los quando os pacotes de ativos forem criados, o que melhorará o desempenho de carregamento dos seus pacotes de ativos e o uso de memória em tempo de execução.

Para fazer isso, você pode definir esses dois sinalizadores ao criar seus pacotes:

Para saber mais sobre gerenciamento de ativos, compartilhar feedback ou interagir com a comunidade e a equipe da Unity , confira o fórum de gerenciamento de ativos.