Eu poderia estar ficando louco com manipuladores de eventos? Estou seguindo o caminho errado com meu design?

12

Acho que decidi que realmente gosto de manipuladores de eventos. Talvez eu esteja sofrendo um pouco de paralisia da análise, mas estou preocupado em tornar meu projeto pesado ou encontrar outras consequências imprevistas para minhas decisões de projeto.

Atualmente, meu mecanismo de jogo faz renderização básica baseada em sprite com uma câmera panorâmica. Meu design se parece um pouco com isso:

SceneHandler

Contém uma lista de classes que implementam a interface SceneListener (atualmente apenas Sprites). Chamadas render () uma vez por tick e envia onCameraUpdate (); mensagens para SceneListeners.

InputHandler

Pesquisa a entrada uma vez por tick e envia uma mensagem "onKeyPressed" simples para InputListeners. Eu tenho um Camera InputListener que contém uma instância de SceneHandler e aciona updateCamera (); eventos com base no que é a entrada.

AgentHandler

Chama ações padrão em qualquer agente (AI) uma vez por tick e verifica a pilha quanto a novos eventos registrados, enviando-os para agentes específicos, conforme necessário.

Portanto, tenho objetos de sprite básicos que podem se mover em torno de uma cena e usar comportamentos rudimentares de direção para viajar. Eu entrei na detecção de colisão e é aqui que não tenho certeza de que a direção que meu projeto está indo é boa. É uma boa prática ter muitos manipuladores de eventos pequenos? Eu imagino indo do jeito que sou que eu teria que implementar algum tipo de CollisionHandler.

Eu estaria melhor com um EntityHandler mais consolidado que lida com IA, atualizações de colisão e outras interações de entidade em uma classe? Ou ficarei bem em implementar muitos subsistemas de manipulação de eventos diferentes que transmitem mensagens entre si com base no tipo de evento que é? Devo escrever um EntityHandler que é simplesmente responsável por coordenar todos esses manipuladores de sub eventos?

Percebo que em alguns casos, como meu InputHandler e SceneHandler, esses são tipos muito específicos de eventos. Uma grande parte do código do meu jogo não se preocupa com a entrada, e uma grande parte não se importa com as atualizações que ocorrem puramente na renderização da cena. Assim, sinto que meu isolamento desses sistemas é justificado. No entanto, estou fazendo esta pergunta abordando especificamente eventos do tipo lógica de jogo.

sensae
fonte

Respostas:

21

O design baseado em eventos envolve principalmente a implementação do padrão de design do Reator .

Antes de começar a projetar componentes, você deve examinar as limitações desse padrão (você pode encontrar muitas informações sobre isso).

O primeiro e mais importante dos problemas é que os manipuladores precisam retornar rapidamente , pois todo mundo que fez algum serius trabalha em estruturas baseadas em GUI e aplicativos baseados em javascript.

Ao desenvolver todos os aplicativos com uma complexidade decente , mais cedo ou mais tarde, você encontrará um manipulador que faz um trabalho complexo que exige tempo .

Nesses casos, você pode distinguir duas situações (não necessariamente mutuamente exclusivas):

Complexas relacionadas a eventos responsabilidades e complexos evento-relacionado responsabilidades.

Responsabilidades complexas relacionadas a eventos:

No primeiro caso, você mesclou muitas responsabilidades em um manipulador de componente único : muitas ações são executadas em resposta a um evento ou há sincronizações em execução, por exemplo.

Nesse caso, a solução é dividir o manipulador em manipuladores diferentes (em objetos diferentes, se necessário) e deixar um identificador acionar eventos em vez de chamar diretamente o código de manipulação. Isso permitirá que um evento de prioridade mais alta seja gerenciado antes, já que seu manipulador principal retornou após anexar eventos à fila de eventos.

Outra solução é deixar uma entidade externa acionar eventos e permitir que outras se inscrevam, se estiver interessado (o evento de temporização é o exemplo mais trivial que você pode generalizar).

Responsabilidades complexas não relacionadas a eventos:

O segundo caso ocorre quando há uma complexidade intrínseca na ação a ser executada após um evento: você precisa calcular um número de fibonacci após um evento, por exemplo.

Aqui, o padrão do reator basicamente falha , vale pouco para dividir o algoritmo de geração de fibonacci em pequenos pedaços que disparam eventos após a terminação, a fim de acionar a próxima etapa.

Aqui, a solução é misturar um projeto com rosca ao reator , a boa notícia é que, se a complexidade for intrínseca (você está lendo a seção correta), é muito provável que seja possível iniciar um segmento independente que faça o trabalho e que precisa saber pouco ou nada sobre o restante dos componentes relacionados ao reator.

Para lidar com esse tipo de situação, achei útil adotar uma fila de tarefas em um pool de encadeamentos flexível .

Quando um manipulador precisa iniciar uma ação longa, ele encapsula essa ação em uma unidade de trabalho para ser colocada na fila de trabalhos. Essa ação encapsulada precisa ter um meio para disparar eventos no encadeamento do reator. O gerenciador de filas de tarefas pode executar em seu próprio encadeamento ou no encadeamento do reator e reagir ao evento "newJob".

O gerenciador de filas de tarefas faz o seguinte:

  • ele aloca uma unidade de trabalho para um encadeamento a partir do pool flexível de encadeamentos usando seu algoritmo de shedule se o pool puder fornecer um encadeamento (existe um encadeamento livre ou é permitida a criação de um novo encadeamento)

  • ouça o próprio pool para ver se uma unidade de trabalho foi concluída para que um encadeamento possa ser recuperado para executar mais uma unidade pendente, se houver.

Usando o mecanismo de acionamento de eventos, uma unidade de trabalho pode acionar eventos para notificar a conclusão, o estado da atualização, avisos, erros ou sempre que necessário. O conjunto flexível de encadeamentos garante um bom uso dos recursos e evita que o encadeamento morto pendure todo o sistema; a fila de tarefas permite escolher qual tipo de prioridade atribuir a diferentes tipos de ações, pois você sabe que elas exigirão uma quantidade consistente de recursos computacionais.

FxIII
fonte
3
+1 por ser muito completo. Alguns links para o leitor curioso seriam incríveis.
28611 Sam
1

Na maioria das situações, o uso de eventos em detrimento de outras opções pode melhorar a velocidade e a descentralização. Eu acho que se você pode usar muitos eventos, deve ir em frente.

anonimamente
fonte