Por que as estruturas de pastas são importantes

JOSE MENDEZ / UNITY TECHNOLOGIESSenior Software Developer
Dec 28, 2023|16 Min
Por que as estruturas de pastas são importantes
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.

Como consultor da equipe de sucesso do cliente , muitas vezes me perguntam: "Construímos nossos pacotes de ativos corretamente?" ou uma variação dessa pergunta. Minha resposta é sempre a mesma: Depende do projeto. Depois converso com os clientes sobre os detalhes. Embora essa resposta seja precisa, ela não fornece nenhum insight para auxiliar projetos futuros.

Embora seja um dilema comum, tenho dificuldade em encontrar uma resposta mais geral para a pergunta (apesar de nossas diretrizes e práticas recomendadas existentes para endereçáveis). Alguns usuários não sabem se os pacotes estão corretos para começar, e meu tempo geralmente se limita a examinar projetos para otimização de memória. Isso significa que não posso dizer o uso pretendido de cada ativo em um projeto. Além disso, à medida que um projeto cresce, pode se tornar impossível para qualquer pessoa responder à mesma pergunta para cada ativo.

Por fim, uma percepção me ocorreu: A questão de construir pacotes de ativos corretamente limita a perspectiva de encontrar uma resposta mais geral. Deixe-me explicar.

Um pacote de ativos ocupa memória para carregar, então se os ativos forem agrupados com outros ativos que não são carregados e descarregados ao mesmo tempo, você não estará usando a memória da forma mais otimizada possível. Portanto, perguntar se um ativo está no pacote certo ou se os pacotes têm os ativos certos é essencialmente a questão de saber se você está carregando os ativos certos no momento certo. A resposta para quando ativos específicos devem ser carregados é qual é o uso pretendido desse ativo, e é por isso que a resposta geralmente é: "Depende do projeto". Na prática, descobri que aprender o uso pretendido do ativo revela como os ativos são carregados na memória e como eles devem ser agrupados.

Portanto, “uso pretendido” é a frase-chave: Quem sabe qual é o uso pretendido para um ativo? Como você comunica essa intenção a todos? E, finalmente, quando isso deve acontecer?

Na minha opinião, há um momento em que a resposta a essa pergunta é cristalina: na criação e modificação de ativos. Seja uma textura específica para um personagem, um shader de iluminação global ou uma malha de árvore para uso em vários níveis do jogo, o criador sabe qual é o uso pretendido e, portanto, é mais adequado para comunicar essa intenção. Os artistas podem comunicar essa intenção implementando uma convenção de nomenclatura de arquivos consistente e agrupando arquivos com os mesmos usos pretendidos na mesma pasta.

Os programadores e outros membros da equipe podem usar essas informações para decidir quando e se um ativo deve ser agrupado com outros ativos que serão carregados simultaneamente. Por isso, o uso pretendido de um ativo deve ficar bem claro – à primeira vista – durante todo o projeto, e o diretório de arquivos atua como a fonte da verdade para todos os membros da equipe.

Nesta postagem do blog, analisarei algumas práticas recomendadas e casos extremos comuns que espero que ajudem você a estruturar melhor projetos futuros. Primeiro, vamos discutir algumas das estruturas de pastas comuns e seus problemas.

Estruturas de pastas

Pela minha experiência, existem quatro tipos de estruturas de pastas: aleatória, por tipo de ativo, por recurso e por finalidade. Destes, o último é de longe o melhor, porque transmite a intenção e, portanto, é mais adequado para uma estratégia de agrupamento ideal.

Aleatório não é algo que vejo com frequência. Isso acontece principalmente com desenvolvedores solo que podem não estar familiarizados com desenvolvimento de software, o que se torna insustentável, por razões óbvias, à medida que o projeto cresce em tamanho ou complexidade. A falta de estrutura, ou uma estrutura aleatória, traz muitos problemas – os ativos são difíceis de encontrar e entender o uso pretendido é praticamente impossível.

A estruturação por tipo de ativo é bastante comum, pois é assim que muitos artistas trabalham nos ativos antes de importá-los para o mecanismo. Se você souber o tipo de ativo, será fácil encontrar sua localização, mas todo o resto ficará obscuro. Mesmo com uma ótima convenção de nomenclatura, pode ser difícil dizer se um personagem, ambiente, interface de usuário ou qualquer combinação dos três requer um shader, textura, malha, etc. específicos. Um diretório de arquivos apropriado não deve ocultar informações, mas revelá-las.

Estruturas de pastas por recurso são raras, mas parecem sensatas à primeira vista. Muitas empresas se dividem em equipes de recursos, então por que não agrupar os dados de forma semelhante? Bem, não é assim que o jogo usa os dados. No passado, vi exemplos, como shaders e áudio, que deveriam ser agrupados, mas como são criados por equipes diferentes, essa verdade fica obscura.

Um diretório de arquivos com uma convenção de nomenclatura clara, orientada pela finalidade dos ativos, contorna esses problemas. Para ilustrar isso, usarei um exemplo de jogo fictício chamado “Dinosaur Brawl”.

Briga de dinossauro

Para os propósitos deste exemplo, Dinosaur Brawl é um jogo de ação e aventura 3D em terceira pessoa onde o jogador escolhe um dinossauro para controlar em um vasto mundo aberto com múltiplos biomas, lutando contra outros dinossauros e criaturas pré-históricas na tentativa de ficar mais forte e passar seus genes para a próxima geração – tudo em uma luta gigantesca para sobreviver à próxima Era Glacial. O jogo foi desenvolvido para dispositivos móveis e alguns dados serão distribuídos como parte do aplicativo original. O restante será baixado de um CDN conforme necessário.

Podemos elaborar uma estrutura geral de pastas para todo o projeto a partir da sinopse acima. Como o jogador pode selecionar e lutar contra outros dinossauros, faz sentido criar uma pasta por dinossauro que conterá todos os recursos específicos daquele dinossauro: malhas, efeitos sonoros, texturas, animações, efeitos de partículas, etc. Vou categorizá-los como Ativos Únicos.

Como é um jogo de ação, ele terá ambientes que são biomas separados. Portanto, devemos criar uma única pasta para cada bioma ou nível do projeto: planícies, desertos, tundra, pântanos, vulcões, etc.

É claro que também haverá recursos cuja presença será necessária para seções inteiras do jogo. Um desses conjuntos são os elementos da interface do usuário. Você pode pensar nesses ativos como Ativos Globais. Assim como criamos uma pasta para todos os Ativos Exclusivos, deve haver uma pasta global que fique no topo da hierarquia de pastas. Por exemplo, uma pasta Global UI, uma pasta Global Dinosaur, uma pasta Global Environment e assim por diante. Dessa forma, todas as coisas compartilhadas por essas seções do jogo são armazenadas em um só lugar.

Ativos compartilhados

Esta categoria de ativos é definida como sendo compartilhada por alguns Ativos Únicos, mas não por todos. Dessa forma, eles não se enquadram nas categorias de Ativos Globais ou Únicos. Para Dinosaur Brawl, um exemplo desse tipo de recurso compartilhado poderia ser aqueles que estão presentes em todos os dinossauros voadores, como partículas, shaders e efeitos sonoros necessários para dar a sensação de voar pelo ar.

O que geralmente acontece é que esses ativos são colocados na pasta do primeiro dinossauro que precisa deles. Infelizmente, isso não descreve com precisão como eles devem ser usados e, portanto, confunde sua intenção. No pior cenário, os ativos são duplicados em cada pacote de dinossauro voador, o que é ineficiente para memória, depuração e tamanho do aplicativo.

A melhor solução é criar uma nova pasta com um nome que indique o uso pretendido, como Dinossauros Voadores. Os detalhes para decidir o local são mais complicados; não há um padrão. Prefiro colocá-los em uma subpasta no mesmo nível das pastas globais e exclusivas dos dinossauros, mas colocá-los com outras pastas exclusivas também é adequado.

Mudanças intencionais

Um caso extremo típico com essa convenção é quando os requisitos do projeto mudam e um ativo que originalmente deveria ser Único se torna Compartilhado. Em nosso exemplo do Dinosaur Brawl , para economizar tempo de desenvolvimento, a decisão foi usar o pré-fabricado Velociraptor como base para todos os outros Raptors (como o Utahraaptor, o Dokataraptor, etc.).

O que os desenvolvedores não percebem, porém, é que quando o prefab do Velociraptor é adicionado a um pacote, todos os ativos do Velociraptor serão baixados quando todos os outros raptors forem carregados, aumentando o tempo de download, mesmo que apenas o prefab esteja sendo usado.

Isso aconteceu porque a intenção do ativo foi alterada e a estrutura da pasta não reflete mais isso. Quando ocorre uma mudança na intenção, a localização e o nome do(s) ativo(s) devem ser atualizados para refletir isso, a fim de manter a consistência e a precisão no sistema. Isso comunica à equipe que cria os pacotes quais ativos devem estar em um pacote “Raptor Compartilhado” e quais devem permanecer no modelo Velociraptor Único.

Uso não intencional

Um dos casos extremos mais comuns e difíceis de corrigir é quando um ativo é usado involuntariamente, de uma forma que nunca foi pretendida. Quando isso acontece, geralmente é um acidente. Por exemplo, alguém tem um prazo a cumprir e usa um ativo já existente no projeto para terminar o trabalho rapidamente.

Considere este cenário: Um artista está adicionando um tutorial para uma futura expansão do Dinosaur Brawl e encontra um shader de “brilho dourado” para acentuar quando os jogadores podem fazer um contra-ataque contra inimigos de elite. O que o artista não sabe é que esse shader é um conteúdo de fim de jogo contra um enorme T-Rex – é um chefe único que tem muitos recursos reunidos. Agora todos esses ativos serão baixados durante o tutorial, em um local onde a maioria deles não será usada. Este pacote é enorme, então o sistema que normalmente baixa recursos menores como este no meio de um jogo fica sobrecarregado, causando desde picos de desempenho até travamentos, porque o recurso não pode ser baixado rápido o suficiente por jogadores com conexões ruins.

O exemplo acima é extremo, mas totalmente realista, e já vi acontecer em mais de um projeto. É por isso que, além da estrutura de pastas, todos os ativos devem ter um nome que comunique sua intenção. Se o shader fosse chamado gold_glow_trex_endgame, por exemplo, ficaria claro qual seria o uso pretendido. Então, durante a depuração, ficaria óbvio que esse recurso não deveria ser carregado durante o tutorial.

Soluções de programação

Se você conhece os Addressables, talvez saiba que Grupos e Rótulos são usados ​​para agrupar e rotular ativos da mesma forma que sugeri no exemplo do jogo acima: usando pastas e convenções de nomenclatura sensatas. Você pode se perguntar: “Por que se preocupar com tudo isso se você pode fazer isso usando Grupos e Rótulos?”

Minha resposta é que você deve fazer as duas coisas. Como expliquei no início, à medida que o número de ativos em um projeto aumenta, fica mais difícil e, eventualmente, impossível para uma pessoa saber como todos os ativos devem ser usados. Saber que os grupos de Addressables ​​devem corresponder à estrutura da pasta pode ser uma forma de confirmar se eles estão configurados corretamente.

Tenho visto muitos dos nossos clientes usarem Asset Bundles sem sistemas complexos de codificação de Addressables ​​como uma solução para esse problema. Por exemplo, eles criarão e manterão uma lista mestre que será usada para criar os pacotes ou verificarão as confirmações de controle de versão para comparar alterações nos pacotes, etc. Minha experiência tem sido que essas soluções não são econômicas a longo prazo. É mais um sistema a ser desenvolvido e mantido, o que cria pontos adicionais de falha. À medida que o projeto cresce, eles cedem a uma infinidade de exceções e casos extremos que projetos de longa duração naturalmente acumulam. Pior de tudo, em um nível fundamental, eles falham porque não têm uma solução para erros do usuário.

Conclusão

Um sistema de pastas e uma convenção de nomenclatura de arquivos bem estruturados devem resultar em uma correspondência de 1 para 1 para pacotes de ativos e grupos endereçáveis. Categorizar arquivos em agrupamentos lógicos e subpastas garante que todos os membros da equipe interpretem e localizem os arquivos uniformemente, mitigando possíveis mal-entendidos e discrepâncias conforme a equipe muda e os requisitos do projeto evoluem. Os criadores de ativos podem facilitar o acesso e a navegação, poupando outros membros da equipe da tarefa demorada de procurar ativos específicos. Uma abordagem sistêmica economiza tempo valioso e minimiza a probabilidade de erros e descuidos. Tornar-se um ponto de referência duradouro, uma fonte de verdade, facilitando o processo de integração de novos membros da equipe e garantindo a viabilidade do projeto ao longo do tempo.

Procurando suporte ou aconselhamento sobre estrutura de pastas? Converse conosco nos fóruns. E confira mais blogs técnicos de desenvolvedores Unity como parte do processo contínuo Tecnologia da série Trenches.