Chiamate di funzione per fotogramma rispetto alla messaggistica guidata dagli eventi in Game Design


11

Il design del gioco tradizionale , per quanto ne so, utilizza il polimorfismo e le funzioni virtuali per aggiornare gli stati degli oggetti di gioco. In altre parole, lo stesso set di funzioni virtuali viene chiamato a intervalli regolari (es: per frame) su ogni oggetto nel gioco.

Di recente, ho scoperto che esiste un altro sistema di messaggistica guidato da eventi disponibile per aggiornare gli stati degli oggetti di gioco. Qui, gli oggetti di solito non vengono aggiornati in base al fotogramma. Invece, viene creato un sistema di messaggistica di eventi altamente efficiente e gli oggetti di gioco vengono aggiornati solo dopo aver ricevuto un messaggio di evento valido.

Event Driven Game Architecture è ben descritta in: Codifica di gioco completa di Mike McShaffry .

Potrei chiedere aiuto per le seguenti domande:

  • Quali sono i vantaggi e gli svantaggi di entrambi gli approcci?
  • Dov'è uno migliore rispetto all'altro?
  • Il design dei giochi Event driven è universale e migliore in tutte le aree? È quindi raccomandato per l'uso anche su piattaforme mombili?
  • Quale è più efficiente e quale è più difficile da sviluppare?

Per chiarire, la mia domanda non riguarda la rimozione completa del polimorfismo dal design di un gioco. Desidero semplicemente comprendere la differenza e trarre vantaggio dall'utilizzo della messaggistica guidata dagli eventi rispetto alle normali chiamate (per frame) alle funzioni virtuali per aggiornare lo stato del gioco.


Esempio: questa domanda ha suscitato un po 'di controversie qui, quindi lasciatemi offrire un esempio: secondo MVC, il motore di gioco è diviso in tre parti principali:

  1. Livello applicazione (comunicazione hardware e SO)
  2. Logica di gioco
  3. Vista di gioco

In un gioco di corse, la Vista di gioco è responsabile del rendering dello schermo il più rapidamente possibile, almeno 30 fps. Game View ascolta anche l'input del giocatore. Ora questo succede:

  • Il giocatore preme il pedale del carburante all'80%
  • GameView costruisce un messaggio "Car 2 Fuel Pedal Pressed to 80%" e lo invia a Game Logic.
  • Game Logic riceve il messaggio, valuta, calcola la posizione e il comportamento della nuova auto e crea i seguenti messaggi per GameView: "Disegna pedale carburante auto 2 premuto 80%", "Accelerazione suono auto 2", "Coordinate auto 2 X, Y" .. .
  • GameView riceve i messaggi e li elabora di conseguenza

dove hai trovato qualche link o riferimento? non conosco questo approccio (ma conosco abbastanza bene il modello di progettazione in generale, e consiglio un buon uso dei principi di orientamento degli oggetti in generale), ma penso che uno basato sugli eventi sia meglio .. pensare all'evoluzione nei sistemi di rete: da polling per chiamate asincrone .. è ottimizzato nei calcoli e nel livello di astrazione
nkint

Ciao Nkint, grazie per il tuo commento. In sostanza, desideravo confrontare la comunicazione guidata dagli eventi e le chiamate con le funzioni virtuali. Modificherò un po 'la mia domanda. A proposito, dai un'occhiata a questo link contenente gli schemi di gioco: gameprogrammingpatterns.com .
Bunkai.Satori,

5
Forse sono stupido, ma come farai a far funzionare un sistema di messaggistica senza usare il polimorfismo? Non hai bisogno di un qualche tipo di classe base (astratta o meno) per definire l'interfaccia del ricevitore di eventi? (Modifica: supponendo che tu non stia lavorando in una lingua con la giusta riflessione.)
Tetrad

2
Inoltre, l'efficienza sarà fortemente legata ai dettagli di implementazione. Non credo che "quale sia più efficiente" sia una domanda a cui si può rispondere nella sua forma attuale.
Tetrad,

1
Gli oggetti di gioco devono spuntare. Gli oggetti di gioco devono comunicare tra loro. Queste due cose devono accadere entrambe. Il primo che non fai con i messaggi perché è ridondante (probabilmente hai un elenco di tutti gli oggetti da qualche parte, basta chiamarli update). Il secondo è possibile fare con la messaggistica per vari motivi.
Tetrad

Risposte:


8

Sono un convinto sostenitore della necessità di essere la madre dell'invenzione. Non mi piace codificare nulla a meno che il suo bisogno sia chiaro e ben definito. Penso che se avvii il tuo progetto impostando un sistema di messaggistica per eventi, lo stai facendo male. Credo che solo una volta che avrai creato e testato la tua infrastruttura e avrai tutti i pezzi del tuo progetto come moduli ben definiti e funzionanti, dovresti concentrarti su come connetterli tra loro. Cercare di progettare un sistema di messaggistica degli eventi prima di comprendere la forma e le esigenze di ciascun modulo è fuori servizio.

Quali sono i vantaggi e gli svantaggi di entrambi gli approcci?

Penso che alcune persone sosterrebbero che ci sono buoni sistemi di messaggistica là fuori pronti per essere installati e utilizzati. Forse è vero. Certamente le prestazioni di una soluzione su misura specificamente adattata alle tue esigenze sarebbero maggiori di un bus di messaggi per tutti gli usi. La grande domanda è: quanto tempo impiegherai a costruire un sistema di messaggistica piuttosto che usare qualcosa che è già stato costruito. Ovviamente, se riuscissi a trovare una sorta di sistema di eventi con esattamente il set di funzionalità di cui hai bisogno, allora è una decisione abbastanza semplice. Questo è il motivo per cui mi assicuro di aver capito esattamente di cosa ho bisogno prima di prendere quella decisione.

Dov'è uno migliore rispetto all'altro?

Potremmo parlare di memoria e risorse qui. Se stai sviluppando per qualsiasi hardware con risorse limitate, è certamente un problema utilizzare un sistema di eventi che lo taglierà in modo significativo. In realtà, tuttavia, penso che il problema sia dovuto a ciò di cui hai bisogno, che come ho già detto è qualcosa che non conosci fino a quando non vedi come appaiono tutti i pezzi. Se hai abbastanza esperienza nella costruzione di questi sistemi che conosci in anticipo esattamente come appariranno tutti i pezzi, allora puoi anche rispondere a questa domanda in anticipo. Immagino che tu non abbia questa esperienza, dal momento che non lo faresti se lo facessi.

Il design dei giochi Event driven è universale e migliore in tutte le aree? È quindi raccomandato per l'uso anche su piattaforme mobili?

Universale e migliore in tutti i settori? Un'affermazione piuttosto generosa come quella viene facilmente respinta. Tutto ciò che aggiunge sovraccarico deve portare la sua quota del carico di lavoro. Se non stai usando abbastanza eventi per giustificare il sovraccarico del design, allora è il design sbagliato.

Quale è più efficiente e quale è più difficile da sviluppare?

L'efficienza dipende da come è stata implementata. Penso che un design tradizionale ben sviluppato supererà un sistema basato su eventi in qualsiasi giorno della settimana. Questo perché la comunicazione tra i pezzi può essere micro-gestita e resa super efficiente. Naturalmente, ciò rende anche più difficile progettare dal punto di vista dell'esperienza e del tempo necessario per completarlo e renderlo più efficiente. D'altra parte, se ti manca questa esperienza o non hai il tempo di sviluppare bene l'applicazione, allora sarà più efficiente utilizzare un design basato su eventi adatto alle tue esigenze. Se hai esigenze di eventi personalizzati che non si adattano facilmente a una struttura basata su eventi, ciò potrebbe rendere la struttura basata su eventi molto difficile da progettare, soprattutto per mantenere efficiente la progettazione.


Ciao Eric, grazie per la tua opinione dettagliata sulla mia domanda. Mentre leggo le tue e altre risposte, l'implementazione dei messaggi degli eventi probabilmente non provoca miracoli nell'aumento complessivo delle prestazioni del gioco. Invece complicherà molto le cose. Vorrei vedere qualche altra risposta, prima di chiudere questa domanda. Poiché l'intero libro trattava questo argomento, sembrava una buona idea da considerare. Non sono molto sicuro se la decisione di utilizzare o meno i messaggi possa essere presa nel mezzo del progetto. Se si usano messaggi di eventi, direi, anche il design complessivo dell'oggetto deve essere modificato.
Bunkai.Satori,

Non sono d'accordo. Se il design complessivo dell'oggetto è ben incapsulato, dovrebbe essere abbastanza flessibile da poter essere utilizzato in qualsiasi tipo di framework. Sono stato in grado di scambiare metodi di comunicazione in grandi progetti con pochissimo lavoro perché ogni pezzo era ben incapsulato.
Erick Robertson,

7

Penso che tu stia confrontando mele e arance qui. Il polimorfismo non è affatto sostituito dalla messaggistica. Probabilmente vorrai che eventi / messaggi colleghino componenti debolmente accoppiati. Per esempio. per inviare un messaggio da un'entità quando si verifica una collisione, per aggiornare il punteggio del giocatore o forse per attivare un effetto sonoro. In modo che queste singole classi non conoscano le altre classi e inviino e / o gestiscano i messaggi.

Ma il vostro gioco è molto probabilmente avrà un qualche aggiornamento loop e dal momento che si dispone di quel ciclo di aggiornamento, può essere facilmente utilizzato per aggiornare tutte le entità di gioco che devono essere aggiornati ogni fotogramma pure. Questo non ti impedisce di usare la messaggistica però ...

Se hai una sorta di struttura in cui aggiungi / rimuovi entità di gioco, puoi anche includerle nel tuo ciclo di aggiornamento invece di inviare loro un messaggio di aggiornamento in ogni frame. Quindi perché non chiamare l' aggiornamento sulle entità di gioco direttamente mentre si utilizza la messaggistica per connettere diversi sottosistemi del gioco? Mi piace anche il concetto di segnale / slot ( vedi esempio qt ) per sistemi simil-evento.

In conclusione: non esiste un approccio migliore, né sono esclusivi.


Ciao Bummzack. Finalmente arrivò la prima risposta. Grazie per questo :-) Quando ho parlato di Event Driven Architecture, intendevo un sistema MVC con tre livelli chiave: Appliaction, GameView, GameLogic. Tutta la comunicazione tra questi tre sarebbe fatta tramite messaggistica. Questo è significativamente diverso dal sistema tradizionale con ciclo di aggiornamento. Ogni sistema ha un'architettura diversa, alcuni vantaggi e svantaggi e costi di prestazioni diversi. Ritengo pertanto che in alcuni settori si sarebbe leggermente meglio. Dopo una buona analisi, dovremmo essere in grado di concludere qual è la migliore.
Bunkai.Satori,

3
Non riesco a capire perché questo è diverso? Avrai bisogno di un ciclo di aggiornamento da qualche parte e che probabilmente aggiornerà il controller se vuoi restare con MVC. Quindi il controller può inviare messaggi al modello e alla vista ... ma poiché il controller di solito conosce view e modello, potrebbe anche aggiornarli direttamente. Ma questo non sostituisce alcun polimorfismo. La tua domanda e le tue assunzioni sembrano molto teoriche .. magari esegui il backup con qualche codice di esempio o alcuni riferimenti?
Bummzack,

Il libro sopra citato, Game Coding Complete, raccomanda la messaggistica degli eventi tramite chiamate dirette a metodi virtuali. Sebbene il sistema di messaggistica appaia più complesso, il suo vantaggio è che ogni oggetto di gioco non ha bisogno di controllare il mondo di gioco. Il mondo di gioco viene controllato dalla logica di gioco una sola volta, quindi vengono indirizzati solo quegli oggetti che dovrebbero cambiare il loro stato. Questa è una differenza e può significare un risparmio sui costi. Se decidi che i tuoi oggetti di gioco comunicheranno tramite messaggi non saranno chiamati per ogni frame, quindi i due approcci si escludono a vicenda.
Bunkai.Satori,

1
Votato per una risposta che riflette i commenti di Tetrad sotto la domanda originale. @ Bunkai.Satori Il "mondo di gioco è controllato solo una volta" è il ciclo di aggiornamento, tutto ciò che necessita di aggiornamento lo ottiene. I messaggi di evento sono pensati per essere fatti raramente ma continuano a essere fondamentali nel motore (PlayerDied, MonsterDied, ecc.) Che il controllo di ogni frame in un ciclo Update () sarebbe uno spreco, ma è molto probabile che vengano generati dal Il ciclo Update () stesso.
James,

1
Se hai la seconda edizione, guarda il capitolo intitolato Controlling the Main Loop. Dovrebbe essere chiamato la stessa cosa nella terza edizione. Questo dovrebbe davvero rispondere alla tua domanda.
Ray Dey,

4

Questo è iniziato come un commento alla risposta di bummzack, ma è diventato lungo.

A meno che non lo rendiate effettivamente asincrono (invio di eventi su un nuovo thread), i vostri oggetti continuano a ricevere il promemoria in modo sincrono. Supponendo che il dispatcher di eventi utilizzi una tabella hash di base con un elenco collegato in una cella, l'ordine sarà l'ordine in cui gli oggetti sono stati aggiunti a quella cella.

Inoltre, a meno che tu non decida di utilizzare i puntatori a funzione per qualche motivo, stai ancora utilizzando chiamate di funzione virtuali poiché ogni oggetto che riceve un messaggio deve implementare IEventListener (o come lo chiami). Gli eventi sono un'astrazione di alto livello che costruisci usando il polimorfismo.

Usa eventi in cui ti trovi a dover chiamare un elenco di metodi per vari eventi in-game al fine di sincronizzare diversi sistemi nel gioco o in cui la reazione di vari oggetti e sistemi in modo che tali eventi non possano essere chiaramente definiti o desideri aggiungere ulteriori reazioni a tali eventi in futuro.

Sono un ottimo strumento, ma come ha detto Bummzack, non dare per scontato che il polimorfismo e gli eventi risolvano lo stesso problema.


Ciao Bearcdp, grazie per la tua risposta. Per me ha senso. C'è una cosa nella mia mente, che vota per i messaggi dell'evento: diciamo, ci sono 200 espulsioni nella scena. Se usi le chiamate di funzione per fotogramma su tutti gli oggetti, devi controllare tutti i 200 oggetti per verificare la precisione di ogni fotogramma (un esempio; ovviamente gli intervalli di chiamata possono essere gestiti.) Con la messaggistica degli eventi, il mondo di gioco viene esaminato una sola volta. Se ci sono 5 collisioni, solo 5 oggetti vengono avvisati dai messaggi sull'evento di collisione, invece di 200 test di rilevazione del collison con chiamate per frame. Qual è la tua opinione?
Bunkai.Satori,

2
esaminare il mondo di gioco ?? cosa significa? Dovrebbe voler dire eseguire gli stessi 200 controlli per isolare il 5 di cui ha bisogno per inviare messaggi. Con il concetto di funzione virtuale, anche il mondo di gioco viene verificato solo una volta (la funzione di ciascun oggetto a sua volta), quindi passa solo ai 5 che richiedono cambiamenti di stato ... senza il sovraccarico di un sistema di messaggistica.
Steve H,

1
Ascolta Steve H, il mondo non si scontra. Uso principalmente eventi per eventi di alto livello, come PlayerJump, EnemyHit. Per gestire l'efficienza del controllo delle collisioni, è necessario scegliere una struttura di dati per organizzare la posizione fisica degli oggetti nel gioco in modo da poter stabilire le priorità di quali oggetti necessitano e non è necessario verificarne la collisione. Dopo aver determinato tutte le coppie di oggetti che si sono scontrati, è possibile inviare un evento con i relativi dati di collisione (i due oggetti, i dati di velocità / posizione / normali, ecc.).
michael.bartnett,

Ciao Steve e Beardcp, grazie per i tuoi commenti. Quello che hai scritto ha senso. Sembra che possano esserci molte possibili implementazioni del sistema di messaggistica degli eventi. Può esserci la pura sostituzione del concetto di funzione virtuale per frame, come dice il libro sopra citato. Tuttavia, il sistema di messaggistica può coesistere anche con il concetto di funzione virtuale per frame, come dici tu.
Bunkai.Satori,

Perché dici "per qualche motivo" in riferimento ai puntatori di funzione? È praticamente come ho implementato un sistema di eventi le volte che ne ho scritto uno da zero (lavoro in linguaggi che hanno funzioni di prima classe ma è la stessa idea; passa la funzione all'oggetto hub eventi e quell'oggetto mappa le funzioni di listener a eventi) quindi mi chiedo se ci sia uno svantaggio di questo approccio.
jhocking

2

Direi che scegliere l'uno o l'altro è piuttosto sbagliato. Alcuni oggetti devono chiamare ogni frame, altri no. Se si desidera eseguire il rendering di un oggetto, è necessario effettuare una chiamata di rendering ad ogni frame. Gli eventi non possono apportare questa modifica. Lo stesso vale per qualsiasi oggetto fisico basato sul tempo: devi chiamarli in ogni frame.

Tuttavia, non eseguo chiamate a tutti i frame per i miei oggetti di gestione degli oggetti, i miei oggetti di input dell'utente o qualcosa del genere. Le chiamate virtuali di massa a ogni oggetto sul frame sono un'idea terribile.

Il design utilizzato per un particolare oggetto dovrebbe essere basato sulle esigenze di quegli oggetti e non su alcune ideologie per il sistema nel suo insieme.

Modificato per essere più chiaro sulla mia prima frase.


Salve DeadMG, mi spiego: nel caso sopra citato, il design è diviso in tre parti principali: livello Appliaction (accesso hardware), Logica di gioco, Vista gioco. Ciò che viene renderizzato per frame base è la vista di gioco. Tuttavia, quando Game View registra un input dell'utente (es. Pedale del freno premuto durante l'inseguimento del gioco) invia un messaggio "Car 2 Brake Pressed" a Game Logic per l'elaborazione. Game Logic valuta questa azione, calcola il comportamento dell'auto e invia un messaggio alla vista di gioco: "Pneumatici auto 2 blocchi", "Auto 2 sposta su X, Y". In questo modo non è necessario controllare per telaio, se il freno è stato premuto su ogni auto.
Bunkai.Satori,

@Bunkai: Quindi hai alcuni progetti che sono guidati dagli eventi e alcuni design che si chiamano ogni frame. Ciò concorda praticamente con la mia risposta.
DeadMG

Sono felice di trovare comprensione con te, dato che questa domanda è stata un po 'controversa qui oggi :-)
Bunkai.Satori,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.