O que você está procurando?
Hero background image
Usar ScriptableObjects como objetos delegados

Esta página explica como usar ScriptableObjects como contêineres lógicos. Ao fazer isso, você pode tratá-los como objetos delegados ou pequenos pacotes de ações que podem ser chamados quando necessário.

Este é o quarto de uma série de seis mini-guias criados para ajudar os desenvolvedores do Unity com a demonstração que acompanha o e-book, Crie uma arquitetura de jogo modular no Unity com ScriptableObjects.

A demonstração é inspirada na mecânica clássica de jogos de fliperama com bolas e pás e mostra como o ScriptableObjects pode ajudá-lo a criar componentes testáveis, dimensionáveis e fáceis de projetar.

Juntos, o e-book, o projeto de demonstração e esses mini-guias fornecem as melhores práticas para o uso de padrões de design de programação com a classe ScriptableObject em seu projeto Unity. Essas dicas podem ajudá-lo a simplificar seu código, reduzir o uso de memória e promover a reutilização do código.

Essa série inclui os seguintes artigos:

Observação importante antes de começar

Antes de mergulhar no projeto de demonstração ScriptableObject e nesta série de mini-guias, lembre-se de que, em sua essência, os padrões de design são apenas ideias. Elas não se aplicam a todas as situações. Essas técnicas podem ajudá-lo a aprender novas maneiras de trabalhar com Unity e ScriptableObjects.

Cada padrão tem prós e contras. Escolha apenas os que beneficiam significativamente seu projeto específico. Seus designers dependem muito do Unity Editor? Um padrão baseado em ScriptableObject pode ser uma boa opção para ajudá-los a colaborar com seus desenvolvedores.

Em última análise, a melhor arquitetura de código é aquela que se adapta ao seu projeto e à sua equipe.

ScriptableObjects
UM OBJETO COM SCRIPT CONTÉM A "ESTRATÉGIA" DENTRO DO MONOCOMPORTAMENTO.
O padrão de estratégia

Usando o padrão de estratégia, você pode definir uma interface ou uma classe ScriptableObject de base e, em seguida, tornar esses objetos delegados intercambiáveis em tempo de execução.

Um aplicativo é encapsular algoritmos para realizar tarefas específicas em um ScriptableObject e, em seguida, usar esse ScriptableObject no contexto de outra coisa.

Por exemplo, se você estivesse escrevendo um sistema de IA ou de localização de caminhos para uma classe EnemyUnit, poderia criar um ScriptableObject com uma técnica de busca de caminhos (como A*, Dijkstra etc.).

A EnemyUnit em si não conteria, de fato, nenhuma lógica de pathfinding. Em vez disso, ele manteria uma referência a um ScriptableObject de "estratégia" separado. O resultado desse design é que você pode mudar para um algoritmo diferente simplesmente trocando os objetos. Essa é uma maneira de escolher comportamentos diferentes em tempo de execução.

Quando o MonoBehaviour precisa executar uma tarefa, ele chama os métodos externos do ScriptableObject em vez dos seus próprios. Por exemplo, o ScriptableObject pode conter métodos públicos para MoveUnit ou SetTarget para conduzir a unidade inimiga e especificar um destino.

Comportamento plugável

Você pode aprimorar esse padrão com uma classe base abstrata ou uma interface. Fazer isso significa que qualquer ScriptableObject que implemente a estratégia pode ser trocado por outro. Esse ScriptableObject com hot-swap "conecta-se" ao MonoBehaviour que o referencia, mesmo em tempo de execução.

Se você precisar que a EnemyUnit altere os comportamentos devido às condições do jogo, o contexto externo (o MonoBehaviour) poderá verificar essas condições. Em seguida, ele pode conectar um ScriptableObject diferente como resposta.

Ao separar os detalhes de implementação em um ScriptableObject, você também pode facilitar uma melhor divisão de responsabilidades entre a sua equipe. Um desenvolvedor pode se concentrar no algoritmo dentro do ScriptableObject, enquanto outro trabalha no contexto do MonoBehaviour.

Para criar esse comportamento conectável, certifique-se de que:

  • Definir uma classe ou interface de base para a estratégia: Essa classe ou interface deve incluir os métodos e as propriedades necessários para executar a estratégia.
  • Crie as classes ScriptableObject: Cada um deles pode oferecer diferentes implementações da estratégia. Por exemplo, você pode criar uma classe que implemente um algoritmo de IA simples e outra classe que implemente um algoritmo mais complexo.
  • Crie um ScriptableObject que implemente a estratégia: Preencha a lógica ausente e preencha o Inspector com os valores necessários.
  • Use a estratégia no contexto: No MonoBehaviour, chame os métodos e as propriedades implementados no ScriptableObject. Para facilitar a chamada desses métodos, passe as dependências como parâmetros.

Organizar seu código dessa forma pode facilitar a alternância entre diferentes implementações da mesma estratégia. Esse comportamento plugável torna-se mais fácil de depurar e manter.

Exemplo: AudioDelegate

Um algoritmo ou estratégia não precisa ser complicado. O projeto PaddleBallSO, por exemplo, demonstra um sistema de reprodução de áudio bastante básico no SimpleAudioDelegate.

A classe abstrata, AudioDelegateSO, define um único método Play que aceita um parâmetro AudioSource. A implementação concreta, então, substitui isso.

A subclasse SimpleAudioDelegateSO define uma matriz de AudioClips. Ele escolhe um clipe aleatório e o reproduz usando a implementação substituída do método Play. Isso adiciona uma variação no tom e no volume em um intervalo personalizado.

Embora sejam apenas algumas linhas, você pode criar muitos efeitos de áudio diferentes com o trecho de código abaixo.

Embora esse exemplo específico não seja realmente adequado para uso intenso de áudio, ele é apresentado aqui como uma demonstração de uso básico de ScriptableObjects em um padrão de estratégia.

Um designer pode criar muitos ScriptableObjects diferentes para representar efeitos sonoros sem tocar no código. Novamente, isso requer suporte mínimo de um desenvolvedor, uma vez que o ScriptableObject básico esteja completo.

No PaddleBallSO, agora é possível configurar um novo conjunto de sons para tocar quando a bola atingir uma das paredes do nível. Os designers ganham independência criativa e flexibilidade porque estão trabalhando inteiramente no Editor. Essa abordagem libera recursos de programação, pois os desenvolvedores não precisam mais ajudar em todas as decisões de design.

Objetos delegados
A CENA DE DEMONSTRAÇÃO DO AUDIODELEGATES MOSTRA COMO OS OBJETOS COM SCRIPT PODEM CONTER LÓGICA.
Demonstração de padrões

Você também pode ver o exemplo de áudio na demonstração de padrões. Cada som deriva de um ativo SimpleAudioDelegateSO ligeiramente diferente, com pequenas variações entre as instâncias.

Neste exemplo, cada canto inclui um AudioSource. Um AudioModifier MonoBehaviour personalizado usa um delegado baseado em ScriptableObject para reproduzir o som.

As diferenças no tom resultam apenas das configurações em cada ativo ScriptableObject (BeepHighPitched_SO, BeepLowPitched_SO, etc.).

O uso de um ScriptableObject para controlar a lógica da ação pode facilitar a experimentação de ideias pela sua equipe de design. Isso permite que um designer trabalhe de forma mais independente de um desenvolvedor.

Gerente de objetivos
O MONOCOMPORTAMENTO DO GERENCIADOR DE OBJETIVOS TESTA AS CONDIÇÕES DE VITÓRIA USANDO OBJETOS COM SCRIPT.
Exemplo: Padrão de estratégia no sistema de objetivos

O projeto PaddleBallSO também usa o padrão de estratégia em seu sistema de objetivos. Embora isso não seja algo que precise variar no tempo de execução, o encapsulamento de cada objeto em um ScriptableObject oferece uma maneira flexível de testar as condições de ganho e perda.

A classe base abstrata, ObjectiveSO, contém valores como o nome do objetivo e se ele foi concluído.

As subclasses concretas, como ScoreObjectiveSO, implementam a lógica real de como concluir cada objetivo. Eles fazem isso substituindo o método CompleteObjective do ObjectiveSO e adicionando a lógica da condição de vitória.

O jogador precisa atingir uma pontuação específica ou derrotar um determinado número de inimigos? Eles precisam chegar a um local específico ou pegar um item específico? Essas são condições de vitória comuns que podem se tornar objetivos baseados em ScriptableObject.

O ObjectiveManager serve como o contexto mais amplo para os ScriptableObjects. Ele mantém uma lista de ObjectiveSOs e depende de cada ScriptableObject para determinar se está completo. Quando cada ObjectiveSO mostra um estado de conclusão, o jogo termina.

Por exemplo, o ScoreObjectiveSO mostra uma maneira de implementar um objetivo de pontuação:

  • Uma estrutura PlayerScore personalizada corresponde ao ID do jogador, a um elemento da interface do usuário na interface e ao valor real da pontuação.
  • Toda vez que o componente ScoreManager é atualizado, o objetivo verifica a condição de vitória.
  • Se a pontuação do jogador atingir ou exceder o m_TargetScore, ele enviará o objeto PlayerScore vencedor como um evento.

O ObjectiveManager só se preocupa com o fato de todos os objetivos fornecidos estarem concluídos. Ele desconhece os detalhes de cada objetivo em si.

Novamente, o objetivo aqui é a modularidade. Isso permite que você personalize cada ObjectiveSO sem afetar os pré-existentes.

O jogo PaddleBallSO realmente tem apenas um objetivo. Se um dos jogadores atingir a meta de pontuação vencedora, o jogo termina.

No entanto, você pode estender isso ou combinar objetivos para criar um sistema de objetivos mais complexo. Faça experiências e veja se você pode criar novos modos de jogo (por exemplo, marcar um número mínimo de pontos antes que o tempo acabe).

Como a lógica é encapsulada em um ScriptableObject, você pode trocar qualquer ObjectiveSO por outro. Para criar uma nova condição de vitória, basta reconfigurar a lista no ObjectiveManager. De certa forma, o objetivo é "conectável" ao contexto circundante.

Observe que um aspecto útil do ObjectiveSO é o evento usado para enviar mensagens entre GameObjects. Em seguida, exploraremos como usar ScriptableObjects para implementar essa arquitetura orientada por eventos.

finalização com script
Mais recursos do ScriptableObject

Leia mais sobre padrões de design com ScriptableObjects no e-book Crie uma arquitetura de jogo modular no Unity com ScriptableObjects. Você também pode saber mais sobre os padrões comuns de design de desenvolvimento do Unity em Eleve o nível de seu código com padrões de programação de jogos.

Você gostou deste conteúdo?