Chamadas de função por quadro versus mensagens orientadas a eventos no design de jogos


11

o design tradicional do jogo , como eu o conheço, usa polimorfismo e funções virtuais para atualizar os estados dos objetos do jogo. Em outras palavras, o mesmo conjunto de funções virtuais é chamado em intervalos regulares (por exemplo, por quadro) em todos os objetos do jogo.

Recentemente, eu descobri, que há outro - sistema de mensagens orientado a eventos disponível para atualizar estados de objetos de jogo. Aqui, os objetos geralmente não são atualizados por quadro. Em vez disso, um sistema de mensagens de eventos altamente eficiente é construído e os objetos do jogo são atualizados somente após o recebimento de uma mensagem de evento válida.

A arquitetura de jogos orientada a eventos está bem descrita em: Game Coding Complete por Mike McShaffry .

Gostaria de pedir ajuda com as seguintes perguntas:

  • Quais são as vantagens e desvantagens das duas abordagens?
  • Onde é melhor um sobre o outro?
  • O design de jogos orientado a eventos é universal e melhor em todas as áreas? Portanto, é recomendado para uso mesmo em plataformas mombile?
  • Qual é mais eficiente e qual é mais difícil de desenvolver?

Para esclarecer, minha pergunta não é sobre remover o polimorfismo completamente de um design de jogo. Eu simplesmente desejo entender a diferença e me beneficiar do uso de mensagens orientadas a eventos versus chamadas regulares (por quadro) para funções virtuais para atualizar o estado do jogo.


Exemplo: Esta questão causou um pouco de controvérsia aqui, então deixe-me dar um exemplo: De acordo com o MVC, o mecanismo de jogo é dividido em três partes principais:

  1. Camada de Aplicação (Comunicação de Hardware e SO)
  2. Game Logic
  3. Visualização do jogo

Em um jogo de corrida, o Game View é responsável por renderizar a tela o mais rápido possível, pelo menos 30 fps. A Visualização do Jogo também escuta as informações do jogador. Agora isso acontece:

  • Jogador pressiona o pedal do combustível a 80%
  • O GameView cria uma mensagem "Pedal de combustível do carro 2 pressionado em 80%" e a envia para a Game Logic.
  • A Game Logic recebe a mensagem, avalia, calcula a posição e o comportamento do carro novo e cria as seguintes mensagens para o GameView: "Draw Car 2 Fuel Pedal Pressed 80%", "Car 2 Sound Acceleration", "Car 2 Coordinates X, Y" .. .
  • O GameView recebe as mensagens e as processa de acordo

Onde você achou? alguns links ou referências? Eu não conheço essa abordagem (mas conheço bastante o padrão de design em geral, e aconselho um bom uso dos princípios de orientação a objetos em geral), mas acho que uma baseada em eventos é melhor. pense na evolução dos sistemas de rede: de polling para chamadas assíncronas .. ele é otimizado em cálculos e no nível de abstração
nkint

Olá Nkint, obrigado pelo seu comentário. Eu basicamente desejava comparar a comunicação orientada a eventos e as chamadas para funções virtuais. Vou modificar um pouco minha pergunta. Entre, dê uma olhada neste link que contém padrões de criação de jogos: gameprogrammingpatterns.com .
Backai.Satori

5
Talvez eu esteja sendo burro, mas como você fará um sistema de mensagens funcionar sem usar polimorfismo? Você não precisará de algum tipo de classe base (abstrata ou não) para definir sua interface de receptor de eventos? (Edit: supondo que você não está trabalhando em uma linguagem com reflexão adequada.)
Tétrada

2
Além disso, a eficiência estará altamente ligada aos detalhes da implementação. Eu não acho que "qual é o mais eficiente" é uma pergunta que pode ser respondida em sua forma atual.
Tétrada

1
Objetos do jogo precisam marcar. Os objetos do jogo precisam se comunicar. Essas duas coisas precisam acontecer. O primeiro que você não faz com as mensagens porque é redundante (você provavelmente tem uma lista de todos os objetos em algum lugar, basta chamá update-los). O segundo você pode fazer com as mensagens por vários motivos.
Tétrada

Respostas:


8

Acredito plenamente na necessidade de ser a mãe da invenção. Não gosto de codificar nada, a menos que sua necessidade seja clara e bem definida. Eu acho que se você iniciar seu projeto configurando um sistema de mensagens de eventos, estará fazendo errado. Acredito que somente depois de criar e testar sua infraestrutura e ter todas as peças do seu projeto bem definidas, os módulos de trabalho devem se concentrar em como você irá conectar esses módulos. Tentar projetar um sistema de mensagens de eventos antes de entender a forma e as necessidades de cada módulo está com defeito.

Quais são as vantagens e desvantagens de ambas as abordagens?

Eu acho que algumas pessoas argumentariam que existem bons sistemas de mensagens por aí prontos para serem instalados e usados. Talvez isso seja verdade. Certamente, o desempenho de uma solução de construção personalizada especificamente adaptada às suas necessidades seria maior do que um barramento de mensagens de uso geral. A grande questão é: quanto tempo você gasta construindo um sistema de mensagens em vez de usar algo que já foi construído. Obviamente, se você pudesse encontrar algum tipo de sistema de eventos com exatamente o conjunto de recursos necessário, será uma decisão bastante fácil. É por isso que eu entendo exatamente o que preciso antes de tomar essa decisão.

Onde é melhor um sobre o outro?

Poderíamos falar sobre memória e recursos aqui. Se você está desenvolvendo para qualquer hardware com recursos limitados, certamente é um problema usar um sistema de eventos que o corte significativamente. Realmente, porém, acho que a questão se resume ao que você precisa, o que, como já mencionei, é algo que você não sabe até ver como são todas as peças. Se você tem experiência suficiente na construção desses sistemas e sabe com antecedência exatamente como serão todas as peças, poderá responder a essa pergunta com antecedência. Acho que você não tem essa experiência, pois não faria essa pergunta se tivesse.

O design de jogos orientado a eventos é universal e melhor em todas as áreas? Portanto, é recomendado para uso mesmo em plataformas móveis?

Universal e melhor em todas as áreas? Uma declaração bastante abrangente como essa é facilmente rejeitada. Qualquer coisa que acrescente sobrecarga precisa transportar sua parte da carga de trabalho. Se você não estiver usando eventos suficientes para garantir a sobrecarga do design, será o design errado.

Qual é mais eficiente e qual é mais difícil de desenvolver?

A eficiência depende de como é implementada. Penso que um design tradicional bem desenvolvido superará o desempenho de um sistema baseado em eventos em qualquer dia da semana. Isso ocorre porque a comunicação entre as peças pode ser microgerenciada e super eficiente. Obviamente, isso também torna mais difícil projetar do ponto de vista da experiência e do tempo necessários para completá-lo e torná-lo mais eficiente. Por outro lado, se você não tiver essa experiência ou não tiver tempo para desenvolver bem o aplicativo, o uso de um design baseado em eventos que atenda às suas necessidades será mais eficiente. Se você tiver necessidades personalizadas de eventos que não se encaixam facilmente em uma estrutura baseada em eventos, isso poderá dificultar o design da estrutura baseada em eventos - especialmente para manter o design eficiente.


Olá Eric, obrigado pela sua opinião detalhada sobre a minha pergunta. Ao ler as respostas de seus e de outros, a implementação de mensagens de eventos provavelmente não causa um milagre no aumento geral do desempenho do jogo. Em vez disso, vai complicar bastante as coisas. Gostaria de ver mais algumas respostas antes de encerrar esta pergunta. Como o livro inteiro abordou esse tópico, parecia uma boa idéia a considerar. Não tenho muita certeza se a decisão de usar ou não as mensagens pode ser tomada no meio do projeto. Se forem usadas mensagens de evento, eu diria que o design geral do objeto também deve ser ajustado.
Backai.Satori

Discordo. Se o design geral do objeto estiver bem encapsulado, ele deverá ser flexível o suficiente para ser usado em qualquer tipo de estrutura. Consegui trocar métodos de comunicação em grandes projetos com muito pouco trabalho, porque cada peça estava bem encapsulada.
Erick Robertson

7

Acho que você está comparando maçãs e laranjas aqui. O polimorfismo não é substituído por mensagens. Você provavelmente desejaria que eventos / mensagens conectassem componentes fracamente acoplados. Por exemplo. enviar uma mensagem de uma entidade quando ocorrer uma colisão, atualizar a pontuação do jogador ou talvez acionar um efeito sonoro. Para que essas classes individuais não conheçam as outras classes e apenas enviem e / ou manejem mensagens.

Mas é provável que o seu jogo tenha um loop de atualização em algum lugar e, como você possui esse loop de atualização, ele pode ser facilmente usado para atualizar todas as entidades do jogo que precisam ser atualizadas a cada quadro também. Isso não impede que você use as mensagens ...

Se você tem algum tipo de estrutura na qual você adiciona / remove entidades do jogo, também pode incluí-las no loop de atualização, em vez de enviar uma mensagem de atualização para cada quadro. Então, por que não chamar a atualização diretamente nas entidades do jogo enquanto você usa as mensagens para conectar diferentes subsistemas do jogo? Também gosto do conceito de Sinal / Slot ( veja o exemplo qt ) para sistemas semelhantes a eventos.

Conclusão: não existe uma abordagem melhor nem exclusiva.


Olá, Bummzack. Finalmente, a primeira resposta chegou. Obrigado por isso :-) Quando mencionei Arquitetura Orientada a Eventos, quis dizer um sistema MVC com três camadas principais: Appliaction, GameView, GameLogic. Toda a comunicação entre esses trhee seria feita por meio de mensagens. Isso é significativamente diferente do sistema tradicional com loop de atualização. Todo sistema possui arquitetura diferente, algumas vantagens e desvantagens e custo de desempenho diferente. Portanto, acredito que alguém seria um pouco melhor em algumas áreas. Após uma boa análise, poderemos concluir qual é o melhor.
precisa saber é o seguinte

3
Não vejo por que isso é diferente? Você precisará de um loop de atualização em algum lugar e isso provavelmente atualizará o Controller se você quiser continuar com o MVC. Em seguida, o controlador pode enviar uma mensagem para o modelo e a visualização. Mas como o controlador geralmente conhece a visualização e o modelo, também pode atualizá-los diretamente. Mas isso não está substituindo nenhum polimorfismo. Suas perguntas e suposições parecem muito teóricas. Talvez apoiá-las com algum código de exemplo ou algumas referências?
bummzack

O livro acima mencionado, Game Coding Complete, recomenda mensagens de eventos através de chamadas diretas a métodos virtuais. Embora o sistema de mensagens pareça mais complexo, sua vantagem é que todo objeto de jogo não precisa verificar o mundo do jogo. O mundo do jogo é verificado pela lógica do jogo apenas uma vez e, em seguida, apenas os objetos são endereçados que devem mudar de estado. Essa é uma diferença e pode significar economia de custos. Se você decidir que seus objetos de jogo se comunicarão por meio de mensagens, eles não serão chamados de cada quadro - portanto, as duas abordagens são mutuamente exclusivas.
Backai.Satori

1
Foi votada uma resposta que reflete os comentários de Tetrad abaixo da pergunta original. @ Bunkai.Satori O 'mundo do jogo é checado apenas uma vez' é o loop de atualização, tudo o que precisa ser atualizado é o que o faz. As Mensagens de Evento devem ser feitas raramente, mas ainda são as principais coisas no mecanismo (PlayerDied, MonsterDied, etc) que verificar todos os quadros em um loop Update () seria um desperdício, mas é mais provável que sejam gerados pelo Loop Update () em si.
James

1
Se você possui a segunda edição, consulte o capítulo chamado Controlando o loop principal. Deve ter o mesmo nome na 3ª edição. Isso realmente deve responder à sua pergunta.
Ray Dey

4

Isso começou como um comentário à resposta de bummzack, mas demorou muito.

A menos que você realmente assíncrono (eventos de despacho em um novo thread), seus objetos ainda estão recebendo o memorando de forma síncrona. Supondo que o distribuidor de eventos use uma tabela de hash básica com uma lista vinculada em uma célula, a ordem será a ordem na qual os objetos foram adicionados a essa célula.

Além disso, a menos que você decida usar ponteiros de função por algum motivo, ainda estará usando chamadas de função virtual, pois cada objeto que recebe uma mensagem deve implementar o IEventListener (ou o que você chamar). Eventos são uma abstração de alto nível que você constrói usando polimorfismo.

Use eventos em que você precise chamar uma lista completa de métodos para vários acontecimentos no jogo, a fim de sincronizar sistemas diferentes no jogo ou em que a reação de vários objetos e sistemas, para que os acontecimentos não possam ser claramente definidos ou você queira para adicionar mais reações aos acontecimentos mencionados no futuro.

Eles são uma ótima ferramenta, mas, como disse Bummzack, não assuma que o polimorfismo e os eventos resolvam o mesmo problema.


Olá Bearcdp, obrigado pela sua resposta. Faz sentido para mim. Há uma coisa em minha mente, que vota nas mensagens do evento: digamos, há 200 objetos na cena. Se você usar chamadas de função por quadro em todos os objetos, deverá verificar todos os 200 objetos para colisão em cada quadro (um exemplo; é claro que os intervalos de chamada podem ser gerenciados.) Com as mensagens de eventos, o mundo do jogo é examinado apenas uma vez. Se houver 5 colisões, apenas 5 objetos serão notificados por mensagens sobre evento de colisão, em vez de 200 testes de detecção de colisão com chamadas por quadro. qual e sua OPINIAO?
precisa saber é o seguinte

2
examinar o mundo do jogo? o que isso significa? Teria que significar executar as mesmas 200 verificações para isolar as 5 sobre as quais ele precisa enviar mensagens. Com o conceito de função virtual, o mundo do jogo também está sendo verificado apenas uma vez (a função de cada objeto) e depois passa para apenas os 5 que precisam de alterações de estado ... sem a sobrecarga de um sistema de mensagens.
26711 Steve H

1
Ouça Steve H, o mundo não se choca. Uso principalmente eventos para eventos de alto nível, como PlayerJump, EnemyHit. Para lidar com a eficiência da verificação de colisão, você precisa escolher uma estrutura de dados para organizar a localização física dos objetos em seu jogo, para poder priorizar quais objetos precisam e não precisam ser verificados quanto à colisão. Depois de determinar todos os pares de objetos que colidiram, você poderá despachar um evento com os dados de colisão relevantes (os dois objetos, dados de velocidade / posição / normais, etc.).
michael.bartnett

Olá Steve e Beardcp, obrigado por seus comentários. O que você escreveu faz sentido. Parece que pode haver muitas implementações possíveis do sistema de mensagens de eventos. Pode haver a substituição pura do conceito de função virtual por quadro, como diz o livro mencionado acima. No entanto, o sistema de mensagens também pode coexistir com o conceito de função virtual por quadro, como você diz.
precisa saber é o seguinte

Por que você diz "por algum motivo" em referência aos ponteiros de função? Foi dessa maneira que eu implementei um sistema de eventos quando escrevi um do zero (trabalho em idiomas que possuem funções de primeira classe, mas é a mesma idéia; passe a função para o objeto do hub de eventos e esse objeto mapeia as funções de ouvinte para eventos), então estou me perguntando se há uma desvantagem nessa abordagem.
Jhocking 01/12/12

2

Eu diria que escolher um ou outro é muito errado. Alguns objetos precisam chamar todos os quadros - outros não. Se você tiver um objeto que deseja renderizar, precisará fazer uma chamada de renderização a cada quadro. Eventos não podem fazer essa alteração. O mesmo vale para qualquer objeto de física baseado no tempo - você deve chamá-lo de todos os quadros.

No entanto, eu não faço chamadas a cada quadro para meus objetos de gerenciamento de objetos, meus objetos de entrada do usuário ou qualquer coisa assim. Chamadas virtuais em massa para todos os objetos no quadro são uma péssima idéia.

O design usado para qualquer objeto em particular deve ser baseado nas necessidades desses objetos e não em alguma ideologia para o sistema como um todo.

Editado para ficar mais claro na minha primeira frase.


Olá DeadMG, deixe-me explicar: No caso mencionado acima, o design é dividido em três partes principais: Camada de aplicação (acesso ao hardware), Game Logic, Game View. O que é renderizado por quadro é a visualização do jogo. No entanto, quando o Game View grava uma entrada do usuário (por exemplo, pedal do freio pressionado no jogo de perseguição), ele envia uma mensagem "Car 2 Brake Pressed" para a Game Logic para processamento. A Game Logic avalia essa ação, calcula o comportamento do carro e envia uma mensagem para a Tela de jogo: "Pneus para carros 2 blocos", "Carro 2 movidos para X, Y". Dessa forma, não é necessário verificar por quadro, se o freio foi pressionado em todos os carros.
precisa saber é o seguinte

@Bunkai: Então você tem alguns projetos que são orientados a eventos e outros que são chamados de cada quadro. Isso praticamente concorda com a minha resposta.
DeadMG

Estou feliz de encontrar compreensão com você, como esta questão foi um pouco controversa aqui hoje :-)
Bunkai.Satori
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.