O que devo considerar ao projetar um sistema do Event Manager?

9

Tenho andado a pensar nos fundamentos de um mecanismo de jogo Java e cheguei ao ponto em que estou pronto para adicionar um sistema do Event Manager.

Eu sei, em teoria , o que um gerente de eventos deve fazer: permitir que objetos sejam "registrados" para determinados eventos e, sempre que o gerente de eventos for notificado de um evento, transmita o evento para os ouvintes "registrados". O que me surpreende é como começar a implementá-lo.

Não consegui encontrar nada, on-line, sobre a implementação de um sistema de eventos do zero, por isso estou procurando informações sobre quais são as melhores práticas neste caso - o que devo ou não fazer.

Por exemplo, é realmente necessário que cada um dos meus objetos de jogo tenha um EventManagercampo? Como todos os meus objetos de jogo são herdados de uma única classe-mãe abstrata, acho que devo usar uma referência estática para que haja apenas uma instância do Gerenciador de Eventos, compartilhada entre todos os objetos de jogo. Faço algo semelhante, com o Applet, que já uso para renderizar cada objeto.

Suponho que eu teria que manter uma coleção de algum tipo para cada possível evento inscrito - adicionando e removendo objetos de jogo da lista, conforme necessário. Eu acho que deveria ser possível criar uma fila de eventos que precisam ser transmitidos; nesse caso, eu poderia simplesmente adicionar "EventManager.Update ()" ao loop principal do jogo e fazer com que o Update()método transmitisse os eventos que ocorreram no final de cada quadro. Por fim, cada objeto teria um HandleEvent(Event e)método, ao qual eles poderiam analisar e responder adequadamente.


Isso soa como a direção apropriada para a implementação de um sistema desse tipo, ou estou fora do caminho e / ou estou perdendo algo bastante óbvio?

Sonhador Corvo
fonte
2
gamedev.stackexchange.com/questions/7718/… Este é um Q / AI fornecido anteriormente que pode ajudá-lo a começar.
James
@ James Ah - parece um bom link. Obrigado! Acho que perdi isso antes.
Raven Dreamer
4
Algumas dicas adicionais: decida se alguns eventos devem efetivamente entrar em vigor imediatamente em vez de no final do quadro, especialmente se um manipulador de eventos acionar eventos adicionais; pense no que acontece quando você recebe loops de eventos; se você for com uma fila, talvez precise de um sistema prioritário ou de uma maneira de contornar a fila.
10136 Samu Hocevar

Respostas:

6

Isso pode ser tão simples quanto você quiser.

for each object in the subscriber list:
    object.notify(this event) // (or 'HandleEvent' if you prefer)

Não tente descobrir o que um gerente de eventos deve fazer - calcule o que você precisa fazer. O resto deve seguir a partir daí, ou pelo menos, deve sugerir algumas perguntas mais específicas.

Kylotan
fonte
3
+1 para "Não tente descobrir o que um X 'deve' fazer - calcule o que você precisa fazer". Na verdade, não adicione um gerenciador de eventos até sentir que precisa de uma maneira desacoplada, mas centralizada, de chamar funções.
@JoeWreschnig Oh Deus, sim =) "resolva o que você precisa fazer" Este é um dos 3 principais conselhos que qualquer desenvolvedor deve seguir.
Patrick Hughes
No começo, eu queria discordar da sua segunda frase sobre não adicionar um gerenciador de eventos, porque a maioria dos jogos pode se beneficiar de uma maneira dissociada, mas centralizada, de chamar funções, mas depois pensei em quantos jogos pequenos que fiz e que não o fizeram tem qualquer tipo de gerenciador de eventos. Então, sim
jhocking 12/12
3

Os três métodos essenciais de que um sistema de eventos precisa são os métodos addListener (), removeListener () e um método para dispatchEvent (). Ou seja, os objetos de método usam para registrar um evento, os objetos de método usam para cancelar o registro e um método para realmente transmitir um evento para todos os ouvintes. Tudo o resto é molho.

Como você observa, certamente precisará de algum tipo de estrutura de dados para acompanhar os ouvintes registrados. A abordagem mais simples seria uma matriz associativa (dicionário ou um objeto em JavaScript) que associa um vetor (ou simplesmente uma matriz, dependendo do idioma) a um evento. Esse vetor é uma lista de todos os ouvintes registrados; adicione ouvintes à lista ou remova-os nos métodos add / removeListener ().

Para transmitir um evento em dispatchEvent (), pode ser tão simples quanto percorrer o vetor. Você pode adicionar mais complexidade ao envio, classificando a prioridade dos eventos ou o que quer que seja, mas não se preocupe com isso até precisar. Em muitos casos, você não precisará disso.

Há um pouco de nuance para dispatchEvent () quando você considera quais dados passar junto com o evento. No nível mais básico, você pode não transmitir dados adicionais; tudo o que o ouvinte precisa saber é que ocorreu um evento. No entanto, a maioria dos eventos possui dados adicionais que os acompanham (por exemplo, onde o evento aconteceu), para que o dispatchEvent () aceite e repasse alguns parâmetros.

Por exemplo, é realmente necessário que cada um dos meus GameObjects tenha um campo "EventManagner"? Como todos os meus GameObjects herdam de uma única classe-mãe abstrata, acho que devo usar uma referência estática para que haja apenas uma instância do EventManager, compartilhada entre todos os objetos do jogo.

Existem várias maneiras de fornecer uma referência a todos os objetos do jogo na classe EventManager. Uma referência estática é certamente uma maneira; outro é com um Singleton. No entanto, essas duas abordagens são bastante inflexíveis; portanto, a maioria das pessoas por aqui recomenda um localizador de serviço ou uma injeção de dependência. Eu tenho feito injeção de dependência; isso significa um campo separado do EventManager para cada objeto, o que você parece querer evitar, mas não sei ao certo por que isso é um problema. Não é como se houvesse muita sobrecarga ao armazenar vários ponteiros.

jhocking
fonte
Sim, a primeira vez que compreendi e implementei a injeção de dependência foi para um objeto EventDispatcher. Depois de colocar isso em prática, tenho vontade de refatorar muitas coisas com esse padrão.
Jhocking 12/10
0

AVISO LEGAL

Eu não sou um programador Java, mas escrevi um artigo explicando como criar um sistema de eventos no C89, uma das linguagens mais fáceis (IMHO), confira

https://prdeving.wordpress.com/2017/04/03/event-driven-programming-with-c-89/

Os princípios básicos da manipulação de eventos são bastante simples.

  • registrar um evento com um retorno de chamada
  • evento de gatilho
  • percorrer ouvintes para ver se há uma correspondência
  • manipulador de incêndio se encontrado
  • repetir

Sabendo disso (mas não sabendo como implementá-lo em Java), é fácil entender que você precisa de alguns manipuladores (funções para manipular os eventos) e adicioná-los a uma matriz (com ponteiros, em C) emparelhada com um nome de evento. Depois de acionar um evento, você armazena o nome do evento acionado e seus argumentos em uma pilha, isso é chamado de pool de eventos.

Então você tem um resumo do evento (um loop simples), que exibe o primeiro evento nessa pilha, tenta encontrar um manipulador para ele e, se houver algum, o executa com os parâmetros.

Obviamente, esse resumo de eventos pode ser acionado sempre que você desejar, por exemplo, uma vez por quadro depois de chamar sua Update()função.

PRDeving
fonte
-2

Para o campo EventManager, use uma variável estática. Um Singleton para o EventManager também seria uma ótima idéia.

Sua abordagem parece boa, mas não se esqueça de torná-la segura para threads.

É bom executar eventos de IO antes ou depois dos "GameEvent" s, portanto, em um quadro, cada "GameEvent" lida com os mesmos dados.

Sibbo
fonte
"Thread-save"? Hmmm
U62 10/10
-1 para a sugestão de singleton totalmente desnecessária e injustificada.