
Como usar um conjunto de tempo de execução baseado em ScriptableObject
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.
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.
Conjuntos de tempo de execução
Os ScriptableObjects podem armazenar dados estáticos. Eles também podem ser modificados para armazenar dados dinâmicos. Isso é particularmente útil para fazer referência a uma coleção de GameObjects ou componentes em tempo de execução.
Um "conjunto de tempo de execução" como esse replica o motivo pelo qual alguns desenvolvedores usam singletons - acesso global fácil. Infelizmente, os singletons vêm com bagagem, como dependências indesejáveis. Essas dependências extras podem complicar os testes e dificultar a depuração.
Como alternativa, considere o uso de um conjunto de tempo de execução baseado em ScriptableObject para componentes referenciados com frequência. Por exemplo, use-os para rastrear elementos do jogo, como inimigos gerados, itens colecionáveis ou pontos de controle.
Como ativos em nível de projeto, os ScriptableObjects fornecem acesso global aos dados de todos os objetos em uma cena. Esse recurso permite o compartilhamento de informações sem muitas das desvantagens associadas aos singletons.
Implementação básica
Esta é uma maneira de criar um conjunto de tempo de execução para GameObjects. Pense nele como um contêiner de dados que mantém uma coleção pública de elementos, mas que também inclui métodos para adicionar e remover membros.
Em tempo de execução, qualquer MonoBehaviour pode fazer referência à propriedade pública Items para obter a lista completa de GameObjects. Outro script ou componente pode chamar Add ou Remove para modificar a lista de itens.
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName = "GameObject Runtime Set", fileName
= "GORuntimeSet")]
public class GameObjectRuntimeSetSO: ScriptableObject
{
private List<GameObject> items = new List<GameObject>();
public List<GameObject> Items => items;
public void Add(GameObject thingToAdd)
{
if (!items.Contains(thingToAdd))
items.Add(thingToAdd);
}
public void Remove(GameObject thingToRemove)
{
if (items.Contains(thingToRemove))
items.Remove(thingToRemove);
}
}
Versão genérica
Você pode tornar um conjunto de tempo de execução mais específico para que ele rastreie apenas um determinado tipo de MonoBehaviour. Nesse caso, crie conjuntos específicos para cada tipo (por exemplo, EnemyRuntimeSet, PickupRuntimeSet etc.).
Uma classe abstrata genérica pode simplificar esse processo. Você poderia então derivar outros conjuntos de tempo de execução dessa classe base. Por exemplo, se você quisesse criar um conjunto de tempo de execução para um componente Enemy personalizado, poderia definir classes para um RuntimeSetSO<T> genérico e um EnemyRuntimeSetSO concreto, como no trecho de código abaixo.
Observe que a classe concreta, EnemyRuntimeSetSO, não tem nenhum detalhe de implementação, apenas um atributo CreateAssetMenu.
Cada novo componente que você deseja rastrear precisa apenas de um conjunto de tempo de execução correspondente para gerenciá-lo. Crie quantas classes de concreto forem necessárias. Inimigos, NPCs, itens de inventário, missões e outros podem ter seus próprios conjuntos de tempo de execução.
public abstract class RuntimeSetSO<T>: ScriptableObject
{
public List<T> Items = new List<T>();
public void Add(T thing)
{
if (!Items.Contains(thing))
Items.Add(thing);
}
public void Remove(T thing)
{
if (Items.Contains(thing))
Items.Remove(thing);
}
}
[CreateAssetMenu(menuName = "Enemy Runtime Set", fileName =
"EnemyRuntimeSet")]
public class EnemyRuntimeSetSO: RuntimeSet<Enemy>
{
}

Uso e limitações
O gerenciamento do conjunto de tempo de execução depende de você. Muitas vezes, você pode usar um segundo MonoBehaviour para configurá-lo ou atualizá-lo durante o jogo.
Se quiser que um componente se inclua automaticamente em um conjunto, você também pode invocar os métodos Add ou Remove do conjunto de tempo de execução a partir dos métodos OnEnable e OnDisable do MonoBehaviour.
Por exemplo, sua classe Enemy fictícia pode se parecer com o trecho de código abaixo.
Aqui, cada componente Enemy pode adicionar ou remover a si mesmo usando seus métodos OnEnable ou OnDisable. Se o campo m_RuntimeSet for definido no Inspector, o componente Enemy aparecerá automaticamente no EnemyRuntimeSetSO. Isso é especialmente útil se você estiver usando o componente Enemy com Prefabs.
Uma limitação dessa técnica é que, se você inspecionar o ScriptableObject em tempo de execução, não poderá ver o conteúdo da lista Items no Inspector por padrão.
Em vez disso, aparece a mensagem "Type mismatch" (Incompatibilidade de tipo) em cada campo de elemento. Por definição, um ScriptableObject não pode serializar um objeto de cena. A lista está realmente funcionando, mas os dados não são exibidos corretamente.
Use uma propriedade pública ou o atributo HideInInspector se quiser evitar confusão e impedir que a lista seja exibida no Inspetor.
Você também pode corrigir esse problema com um script de editor e um inspetor personalizados. Bons exemplos incluem o padrão de arquitetura Soap - ScriptableObject na Unity Asset Store ou o exemplo de demonstração de padrões abaixo.
public class Enemy: MonoBehaviour
{
[SerializeField] private EnemyRuntimeSetSO m_RuntimeSet;
private void OnEnable()
{
m_RuntimeSet.Add(this);
}
private void OnDisable()
{
m_RuntimeSet.Remove(this);
}
}

Demonstração de padrões
Embora o PaddleBallSO não apresente um caso de uso para conjuntos de tempo de execução, a demonstração de padrões do projeto inclui uma pequena amostra que mostra esse padrão de design em funcionamento.
Clique no botão Spawn Block para preencher a parede de blocos de forma incremental. A bola destrói um bloqueio no contato.
Selecione Select Set para mostrar o BlockRuntimeSet_Data no Inspector. Contém a lista de todos os blocos da cena.
Você pode preencher continuamente os blocos na grade tão rapidamente quanto a bola os remove. Ao mesmo tempo, o conjunto de tempo de execução rastreia o número de blocos que está mudando ativamente na lista de itens.
A demonstração do Patterns mostra alguns aprimoramentos de qualidade de vida ao trabalhar com conjuntos de tempo de execução:
- Inspetor personalizado: Como os ScriptableObjects não podem serializar objetos de cena fora da caixa, o script GameObjectRuntimeSetEditor fornece um Inspector personalizado que substitui o erro padrão de incompatibilidade de tipos. Clique em qualquer bloco no Inspector para destacá-lo na hierarquia da cena.
- Atualizar eventos: O conjunto de tempo de execução tem uma System.Action chamada ItemsChanged. Isso notifica o script do Editor ao adicionar ou remover membros da lista de Itens. Isso atualiza o Inspetor quando a lista de Itens é alterada. Um canal de eventos também sincroniza o número atual de itens com um componente RuntimeSetCounter personalizado. Isso mostra a contagem total de blocos na interface do usuário na tela.

Como os conjuntos de tempo de execução ajudam
Da próxima vez que você precisar de uma lista centralizada de GameObjects ou componentes, considere os conjuntos de tempo de execução. Eles podem ser menos dispendiosos do que uma operação de localização(Object.FindObjectOfType ou GameObject.FindWithTag) e menos problemáticos do que os singletons.
Como ScriptableObjects, eles são desacoplados de GameObjects individuais na cena. Mantenha quantos conjuntos de tempo de execução forem necessários para qualquer coisa que você queira rastrear com frequência no tempo de execução.
Os conjuntos de tempo de execução podem simplificar a forma como você monitora os objetos de jogabilidade sem incorrer em dependências desnecessárias. Você pode usar o tempo economizado durante a depuração e os testes para desenvolver novas experiências de jogo para seus jogadores.

Mais recursos do ScriptableObject
Esperamos que os conjuntos de tempo de execução possam beneficiar seus próximos projetos.
Leia mais sobre padrões de design com ScriptableObjects em nosso e-book técnico, 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?
Sim!