Gerenciando estado e eventos entre componentes no GameObject
Link para o projetoComo todos sabem, mais ou menos familiarizados com a plataforma Unity, cada objeto de jogo
GameObject consiste em componentes (embutidos ou personalizados, que geralmente são chamados de "scripts"). Os componentes são
herdados da classe base
MonoBehavior .

E geralmente, bem ou frequentemente, é feito um link direto para vincular os componentes.

I.e. em um componente, para obter dados de outro componente, obtemos o último usando o método
GetComponent <...> () , por exemplo:

Neste exemplo, uma referência a um componente do tipo
SomeComponent será colocada na variável
someComponent .
Com essa abordagem "fortemente acoplada", especialmente quando há um grande número de componentes, é muito fácil ficar confuso e manter a integridade dessa conexão. Por exemplo, se o nome de uma propriedade ou método em um componente mudar, será necessário corrigi-lo em todos os componentes que o usam. E isso é hemorragia.
Sob o corte muitas fotosCriando uma solução baseada na "forte conexão" dos componentes
Criaremos um projeto vazio para reproduzir a situação usual quando tivermos certos componentes e cada um deles se referir um ao outro, para receber dados ou controlar.
Adicionei dois scripts,
FirstComponent e
SecondComponent , que serão usados como componentes no objeto do jogo:

Agora vou definir uma estrutura simples para cada um dos componentes necessários para os experimentos.


Agora imagine uma situação na qual precisaríamos obter os valores dos campos
state1 do componente
FirstComponent e chamar seu
método ChangeState (...) no componente
SecondComponent . Para fazer isso, você precisa obter um link para o componente e solicitar os dados necessários no componente
SecondComponent :

Após iniciarmos o jogo no console, veremos que recebemos dados do
FisrtComponent do
SecondComponent e alteramos o estado do primeiro

Agora, exatamente da mesma maneira, podemos obter os dados e na direção oposta do componente
FirstComponent para obter os dados do componente
SecondComponent .

Após o início do jogo, também será visível que estamos recebendo dados e podemos controlar o componente
SecondComponent do
FirstComponent .

Este foi um exemplo bastante simples e, para entender que tipo de problema eu quero descrever, seria necessário complicar bastante a estrutura e os relacionamentos de todos os componentes, mas o significado é claro. Agora a conexão entre os componentes é a seguinte:


Expandir até mesmo um objeto de jogo com novos componentes, se eles precisarem interagir com os existentes, será bastante rotineiro. E especialmente se, por exemplo, o nome do campo
state1 no componente
FirstComponent for alterado, por exemplo, para
state_1 e você precisar alterar o nome em que é usado em todos os componentes. Ou quando o componente tem muitos campos, torna-se bastante difícil navegá-los.
Criando uma solução baseada no "estado geral" entre componentes
Agora imagine que não precisaríamos obter um link para cada componente de interesse e obter dados dele, mas haveria um certo objeto que contém os estados e dados de todos os componentes no objeto do jogo. No diagrama, ficaria assim:

Estado geral ou Objeto de estado geral (SharedState) também é um componente que desempenhará a função de um componente de serviço e armazenará o estado de todos os componentes do objeto de jogo.
Vou criar um novo componente e denominar SharedState:

E determinarei o código para esse componente universal. Ele armazenará um dicionário e um indexador fechados para um trabalho mais conveniente com o dicionário de componentes, também será um encapsulamento e não funcionará diretamente com o dicionário de outros componentes.

Agora, esse componente precisa ser colocado no objeto do jogo para que os outros componentes possam acessá-lo:

Em seguida, é necessário fazer algumas alterações nos componentes
FirstComponent e
SecondComponent para que eles usem o componente
SharedState para armazenar seus estados ou dados:


Como você pode ver no código do componente, não armazenamos mais o campo. Em vez disso, usamos o estado geral e temos acesso a seus dados usando a tecla “state1” ou “counter”. Agora, esses dados não estão vinculados a nenhum componente e, se um terceiro componente aparecer, tendo acesso ao SharedState, ele poderá acessar todos esses dados.
Agora, para demonstrar a operação desse esquema, você precisa alterar os métodos de Atualização nos dois componentes. No
FisrtComponent :

E no componente
SecondComponent :

Agora, os componentes não sabem a origem desses valores, ou seja, antes de recorrerem a algum componente específico para obtê-los, e agora eles são simplesmente armazenados em um espaço comum e qualquer componente tem acesso a eles.
Depois de iniciar o jogo, você pode ver que os componentes obtêm os valores desejados:

Agora que sabemos como ele funciona, podemos derivar a infraestrutura básica para acessar o estado geral na classe base, para não fazer tudo isso em cada componente separadamente:

E vou torná-lo abstrato, para não criar acidentalmente uma instância dela ... E também é aconselhável adicionar um atributo indicando que esse componente base requer o componente
SharedState :

Agora você precisa alterar os componentes
FirstComponent e
SecondComponent para que eles herdem do
SharedStateComponent e remova todos os desnecessários:


Ok E quanto a chamar métodos? Propõe-se fazer isso também não diretamente, mas através do padrão Publisher-Subscriber. Simplificado.
Para implementar isso, você precisa adicionar outro componente comum, semelhante ao que contém os dados, exceto que este conterá apenas assinaturas e será chamado
SharedEvents :

O princípio é o seguinte. Um componente que deseja chamar algum método para outro componente não fará isso diretamente, mas chamando um evento, apenas pelo nome, à medida que obtemos dados do estado geral.
Cada componente assina alguns eventos que está pronto para rastrear. E se ele capturar esse evento, ele executará o manipulador, que é definido no próprio componente.
Crie o componente
SharedEvents :

E definiremos a estrutura necessária para gerenciar assinaturas e publicações.

Para a troca de dados entre assinaturas, publicações, é definida uma classe base, uma determinada será determinada para o autor de cada evento de forma independente e, em seguida, vários exemplos serão definidos:

Agora você precisa adicionar um novo componente ao objeto do jogo:

e expanda um
pouco a classe base
SharedStateComponent e inclua o requisito de que o objeto contenha
SharedEvents

Assim como o objeto de estado geral, o objeto de assinatura geral deve ser obtido no objeto de jogo:


Agora, definimos uma assinatura de evento, que processaremos em
FisrtComponent e uma classe para transferir dados através desse tipo de evento, e também
alteramos SecondComponent para que o evento dessa assinatura seja publicado:


Agora, inscrevemos-se em qualquer evento chamado “writeomedata” no componente
FirstComponent e simplesmente
imprima uma mensagem no console quando ocorrer. E surge neste exemplo invocando a publicação de um evento com o nome "writeomedata" no componente
SecondComponent e transferindo algumas informações que podem ser usadas no componente que captura eventos com esse nome.
Depois de iniciar o jogo em 5 segundos, veremos o resultado do processamento do evento no
FirstComponent :

Sumário
Agora, se você precisar expandir os componentes desse objeto de jogo, que também usará o estado geral e os eventos gerais, será necessário adicionar uma classe e simplesmente herdar de
SharedStateComponent :
Continuação do tópico