O que você está procurando?

6 maneiras pelas quais os ScriptableObjects podem beneficiar sua equipe e seu código

THOMAS KROGH-JACOBSEN / UNITY TECHNOLOGIESProduct Marketing Core Tech
Apr 20, 2023|17 Min
6 maneiras pelas quais os ScriptableObjects podem beneficiar sua equipe e seu código
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.

Temos o prazer de anunciar que lançamos um novo e-book técnico, Crie uma arquitetura de jogo modular no Unity com ScriptableObjectsque oferece práticas recomendadas de desenvolvedores profissionais para a implantação de ScriptableObjects na produção.

Junto com o e-book, você pode fazer o download de um projeto de demonstração do GitHub inspirado na mecânica clássica de jogos de arcade com bola e raquete. A demonstração mostra como os ScriptableObjects podem ajudá-lo a criar componentes testáveis e dimensionáveis, além de serem fáceis de projetar. Embora um jogo como esse pudesse ser criado com muito menos linhas de código, esta demonstração mostra o ScriptableObjects em ação.

Uma demonstração com bola e remo acompanha o e-book.
Uma demonstração com bola e remo acompanha o e-book.

Esta postagem explica os benefícios dos ScriptableObjects, mas não aborda os conceitos básicos ou a codificação geral no Unity. Se você não tem experiência em programação no Unity, visite o Unity Learn, que oferece tutoriais introdutórios úteis. O primeiro capítulo do e-book também oferece uma sólida cartilha.

Vejamos seis maneiras pelas quais você pode se beneficiar do uso de ScriptableObjects em seus projetos. Quer saber mais? Todos esses exemplos são explorados mais detalhadamente no e-book e no projeto de demonstração.

1. Colaboração eficiente da equipe

Embora muitas das técnicas compartilhadas aqui também possam ser obtidas com o uso de classes C#, um dos principais benefícios dos ScriptableObjects é a acessibilidade a artistas e designers. Eles podem usar ScriptableObjects para configurar e aplicar a lógica do jogo em um projeto sem precisar editar o código.

O Editor torna conveniente a visualização e a edição de ScriptableObjects, permitindo que os designers configurem dados de jogabilidade sem o suporte pesado da equipe de desenvolvedores. Isso também se aplica à lógica do jogo, como a aplicação de comportamento a um NPC adicionando um ScriptableObject (explicado nos padrões abaixo).

O armazenamento de dados e lógica em um único MonoBehaviour pode resultar em conflitos de mesclagem demorados se duas pessoas alterarem partes diferentes do mesmo Prefab ou cena. Ao dividir os dados compartilhados em arquivos e ativos menores com ScriptableObjects, os designers podem criar a jogabilidade em paralelo com os desenvolvedores, em vez de ter que esperar que estes últimos terminem de configurar a jogabilidade no código antes de testá-la.

Podem surgir problemas quando colegas com funções diferentes acessam o código e os recursos do jogo ao mesmo tempo. Com os ScriptableObjects, o programador pode controlar a parte do projeto que pode ser editada no Editor. Além disso, o uso de ScriptableObjects para organizar seu código leva naturalmente a uma base de código mais modular e eficiente para testes.

Saiba como um designer de jogos usa ScriptableObjects

Christo Nobbs, um designer técnico sênior de jogos especializado em design de jogos de sistemas e Unity (C#), contribuiu para O manual do designer de jogos em Unitye é o principal autor de uma série de postagens de blog sobre o design de sistemas de jogos em Unity. Suas postagens, "Systems that create ecosystems: Design de jogos emergentes" e "Imprevisivelmente divertido: O valor da randomização no design de jogos" fornece exemplos interessantes de como os designers podem usar ScriptableObjects.

2. Contêineres de dados

A modularidade é um princípio geral de software que pode ser implementado em C# sem o uso de ScriptableObjects. Mas, conforme mencionado acima, os ScriptableObjects ajudam a promover práticas de codificação limpas, separando os dados da lógica, o que é um primeiro passo em direção ao código modular do jogo. Essa separação significa que é mais fácil fazer alterações sem causar efeitos colaterais indesejados e melhora a capacidade de teste.

Os ScriptableObjects são excelentes no armazenamento de dados estáticos, o que os torna úteis para configurar valores estáticos de jogabilidade, como itens ou estatísticas de NPCs, diálogos de personagens e muito mais. Como os ScriptableObjects são salvos como um ativo, eles persistem fora do modo de jogo, o que possibilita usá-los para carregar em uma configuração estática que muda dinamicamente no tempo de execução.

Embora as alterações nos dados do ScriptableObject persistam no Editor, é importante observar que eles não foram projetados para salvar dados do jogo. Nesse caso, é melhor usar um sistema de serialização, como JSON, XML, ou uma solução binária, se o desempenho for crítico.

Os MonoBehaviours têm uma sobrecarga extra, pois exigem um GameObject - e, por padrão, uma Transform - para atuar como host. Isso significa que você precisa criar muitos dados não utilizados antes de armazenar um único valor. Um ScriptableObject reduz esse espaço de memória e elimina o GameObject e o Transform. Ele também armazena dados no nível do projeto, o que é útil se você precisar acessar os mesmos dados de várias cenas.

É comum ter muitos GameObjects que dependem de dados duplicados que não precisam ser alterados no tempo de execução. Em vez de ter esses dados locais duplicados em cada GameObject, você pode canalizá-los para um ScriptableObject. Cada um dos objetos armazena uma referência ao ativo de dados compartilhado, em vez de copiar os próprios dados. Isso pode proporcionar melhorias significativas de desempenho em projetos com milhares de objetos.

Muitos objetos com dados locais duplicados levam a ineficiências de desempenho.
Muitos objetos com dados locais duplicados levam a ineficiências de desempenho
Muitos objetos compartilham dados (em vez de duplicá-los) por meio de um ScriptableObject
Muitos objetos compartilham dados (em vez de duplicá-los) por meio de um ScriptableObject

No design de software, essa é uma otimização conhecida como padrão flyweight. Reestruturar seu código dessa forma, usando ScriptableObjects, evita a cópia de valores e reduz o consumo de memória. Confira nosso e-book, Eleve o nível de seu código com padrões de programação de jogos para saber mais sobre o uso de padrões de design no Unity.

3. Enums

Um bom exemplo de como os ScriptableObjects podem simplificar seu código é usá-los como enums para operações de comparação. O ScriptableObject pode representar uma categoria ou um tipo de item, como um efeito de dano especial - frio, calor, elétrico, mágico etc.

Se o seu aplicativo exigir um sistema de inventário para equipar itens de jogo, os ScriptableObjects poderão representar tipos de itens ou slots de armas. Os campos no Inspector funcionam então como uma interface de arrastar e soltar para configurá-los.

Categorias baseadas em ScriptableObject de arrastar e soltar
Categorias baseadas em ScriptableObject de arrastar e soltar

O uso de ScriptableObjects como enums torna-se mais interessante quando você deseja estendê-los e adicionar mais dados. Diferentemente dos enums normais, os ScriptableObjects podem ter campos e métodos adicionais. Não há necessidade de ter uma tabela de pesquisa separada ou de correlacionar com uma nova matriz de dados.

Enquanto os enums tradicionais têm um conjunto fixo de valores, os enums ScriptableObject podem ser criados e modificados em tempo de execução, permitindo que você adicione ou remova valores conforme necessário.

Se você tiver uma longa lista de valores de enum sem numeração explícita, a inserção ou remoção de um enum pode alterar sua ordem. Essa reordenação pode introduzir bugs sutis ou comportamentos não intencionais. Os enums baseados em ScriptableObject não têm esses problemas. Você pode excluir ou adicionar ao seu projeto sem precisar alterar o código todas as vezes.

Suponha que você queira tornar um item equipável em um RPG. Você poderia acrescentar um campo booleano extra ao ScriptableObject para fazer isso. Certos personagens não podem ter certos itens? Alguns itens são mágicos ou têm habilidades especiais? As enums baseadas em ScriptableObject podem fazer isso.

4. Objetos delegados

Como você pode criar métodos em um ScriptableObject, eles são tão úteis para conter lógica ou ações quanto para armazenar dados. Mover a lógica do seu MonoBehaviour para um ScriptableObject permite que você use o último como um objeto delegado, tornando o comportamento mais modular.

Se você precisar executar tarefas específicas, poderá encapsular os algoritmos em seus próprios objetos. O Gang of Four original se refere a esse design geral como o padrão de estratégia. O exemplo abaixo mostra como tornar o padrão de estratégia mais útil usando uma classe abstrata para implementar o EnemyAI. O resultado são vários ScriptableObjects derivados com comportamento diferente, que se torna um comportamento conectável, pois cada ativo é intercambiável. Basta arrastar e soltar o ScriptableObject de sua escolha no MonoBehaviour.

Os comportamentos plugáveis podem ser alterados em tempo de execução ou no Editor.
Os comportamentos plugáveis podem ser alterados em tempo de execução ou no Editor

Para obter um exemplo detalhado que mostre como usar ScriptableObjects para impulsionar o comportamento, assista à série de vídeos Pluggable AI with ScriptableObjects. Essas sessões demonstram um sistema de IA baseado em máquina de estado finito que pode ser configurado usando ScriptableObjects para estados, ações e transições entre esses estados.

5. Canais de eventos

Um desafio comum em projetos maiores é quando vários GameObjects precisam compartilhar dados ou estados, evitando referências diretas entre esses objetos. O gerenciamento dessas dependências em escala pode exigir um esforço significativo e, muitas vezes, é uma fonte de bugs. Muitos desenvolvedores usam singletons - uma instância global de uma classe que sobrevive ao carregamento da cena. No entanto, os singletons introduzem estados globais e dificultam os testes de unidade. Se estiver trabalhando com um Prefab que faz referência a um singleton, você acabará importando todas as suas dependências apenas para testar uma função isolada. Isso torna seu código menos modular e eficiente para depuração.

Uma solução é usar eventos baseados em ScriptableObject para ajudar seus GameObjects a se comunicarem. Nesse caso, você está usando ScriptableObjects para implementar uma forma do padrão de design de observador, em que um sujeito transmite uma mensagem a um ou mais observadores fracamente desacoplados. Cada objeto observador pode reagir independentemente do sujeito, mas não tem conhecimento dos outros observadores. O assunto também pode ser chamado de "editor" ou "transmissor" e os observadores de "assinantes" ou "ouvintes".

Você pode implementar o padrão de observador com MonoBehaviours ou objetos C#. Embora isso já seja uma prática comum no desenvolvimento do Unity, uma abordagem somente de script significa que seus designers dependerão da equipe de programação para cada evento necessário durante o jogo.

O canal de eventos baseado em ScriptableObject
O canal de eventos baseado em ScriptableObject

À primeira vista, parece que você adicionou uma camada de sobrecarga ao padrão do observador, mas essa estrutura oferece algumas vantagens. Como os ScriptableObjects são ativos, eles são acessíveis a todos os objetos em sua hierarquia e não desaparecem no carregamento da cena.

O acesso fácil e persistente a determinados recursos é o motivo pelo qual muitos desenvolvedores usam singletons. Os ScriptableObjects geralmente podem oferecer os mesmos benefícios sem introduzir tantas dependências desnecessárias.

Em eventos baseados em ScriptableObject, qualquer objeto pode servir como publicador (que transmite o evento) e qualquer objeto pode servir como assinante (que escuta o evento). O ScriptableObject fica no meio e ajuda a retransmitir o sinal, agindo como um intermediário centralizado entre os dois.

Uma maneira de pensar sobre isso é como um "canal de eventos". Imagine o ScriptableObject como uma torre de rádio que tem qualquer número de objetos ouvindo seus sinais. Um MonoBehaviour interessado pode se inscrever no canal de eventos e responder quando algo acontecer.

A demonstração mostra como o padrão de observador ajuda você a configurar eventos de jogo para interface do usuário, sons e pontuação.

6. Conjuntos de tempo de execução

Em tempo de execução, você frequentemente precisará rastrear uma lista de GameObjects ou componentes em sua cena. Por exemplo, uma lista de inimigos é algo que você precisa acessar com frequência, mas também é uma lista dinâmica que muda à medida que mais inimigos são gerados ou derrotados. O singleton oferece acesso global fácil, mas tem várias desvantagens. Em vez de usar um singleton, considere armazenar dados em um ScriptableObject como um "Runtime Set". A instância do ScriptableObject aparece no nível do projeto, o que significa que pode armazenar dados que estão disponíveis para qualquer objeto de qualquer cena, oferecendo acesso global semelhante. Como os dados estão localizados em um ativo, sua lista pública de itens pode ser acessada a qualquer momento.

Nesse caso de uso, você obtém um contêiner de dados especializado que mantém uma coleção pública de elementos, mas também fornece métodos básicos para adicionar e remover da coleção. Isso pode reduzir a necessidade de singletons e melhorar a capacidade de teste e a modularidade.

Um Runtime Set fornece acesso global a uma coleção de dados.
Um Runtime Set fornece acesso global a uma coleção de dados

A leitura de dados diretamente de um ScriptableObject também é mais otimizada do que a pesquisa na hierarquia da cena com uma operação de localização como Object.FindObjectOfType ou GameObject.FindWithTag. Dependendo do seu caso de uso e do tamanho da hierarquia, esses métodos são relativamente caros e podem ser ineficientes para atualizações por quadro.

Há várias estruturas de ScriptableObjects que oferecem mais casos de uso do que esses seis cenários. Algumas equipes decidem usar ScriptableObjects extensivamente, enquanto outras limitam seu uso ao carregamento de dados estáticos e à separação da lógica dos dados. Em última análise, as necessidades do seu projeto determinarão como usá-los.

Série avançada para programadores Unity

Crie uma arquitetura de jogo modular no Unity com ScriptableObjects é o terceiro guia de nossa série para programadores de Unity intermediários a avançados. Cada guia, de autoria de programadores experientes, fornece práticas recomendadas para tópicos importantes para as equipes de desenvolvimento.

Criar um guia de estilo C#: Escreva um código mais limpo e escalonável ajuda você a desenvolver um guia de estilo para unificar sua abordagem na criação de uma base de código mais coesa.

Eleve o nível de seu código com padrões de programação de jogosdestaca as melhores práticas para usar os princípios SOLID e os padrões de programação comuns para criar uma arquitetura de código de jogo escalável em seu projeto Unity.

Criamos esta série para fornecer dicas práticas e inspiração para nossos criadores experientes, mas não são livros de regras. Há muitas maneiras de estruturar seu projeto Unity, e o que pode parecer um ajuste natural para um aplicativo pode não ser para outro. Avalie as vantagens e desvantagens de cada recomendação, dica e padrão com seus colegas antes de implementá-los.

Encontre guias e artigos mais avançados no hub de práticas recomendadas do Unity.

Capa do e-book "Crie uma arquitetura de jogo modular no Unity com ScriptableObjects"
"Crie uma arquitetura de jogo modular no Unity com ScriptableObjects" ebook