Gerenciando estado e eventos entre componentes no GameObject

Gerenciando estado e eventos entre componentes no GameObject


Link para o projeto

Como 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 fotos

Criando 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

Source: https://habr.com/ru/post/pt438554/


All Articles