Was muss ich beim Entwerfen eines Event Manager-Systems beachten?

9

Ich habe mich mit den Grundlagen einer Java-Game-Engine beschäftigt und bin an dem Punkt angelangt, an dem ich bereit bin, ein Event Manager-System hinzuzufügen.

Ich weiß theoretisch , was ein Event Manager tun sollte: Erlauben Sie Objekten, sich für bestimmte Ereignisse zu "registrieren", und senden Sie das Ereignis an die "registrierten" Listener, wenn der Event Manager über ein Ereignis benachrichtigt wird. Ich bin ratlos darüber, wie ich mit der Implementierung beginnen soll.

Ich konnte online nichts über die Implementierung eines Ereignissystems von Grund auf finden. Daher suche ich nach Informationen zu den Best Practices in diesem Fall - was ich tun sollte und was nicht .

Ist es zum Beispiel wirklich notwendig, dass jedes meiner Spielobjekte ein EventManagerFeld hat? Da alle meine Spielobjekte von einer einzelnen abstrakten übergeordneten Klasse erben, sollte ich in der Lage sein, eine statische Referenz zu verwenden, damit nur eine Instanz des Event Managers von allen Spielobjekten gemeinsam genutzt wird. Mit dem Applet mache ich etwas Ähnliches, mit dem ich bereits jedes Objekt rendere.

Ich nehme an, ich müsste für jedes mögliche abonnierte Ereignis eine Art Sammlung verwalten - Spielobjekte nach Bedarf hinzufügen und aus der Liste entfernen. Ich denke, es sollte möglich sein, eine Warteschlange mit Ereignissen zu erstellen, die gesendet werden müssen. In diesem Fall könnte ich einfach "EventManager.Update ()" zur Hauptspielschleife hinzufügen und die Update()Methode die Ereignisse senden lassen, die am Ende aufgetreten sind jedes Rahmens. Schließlich hätte jedes Objekt eine HandleEvent(Event e)Methode, die es dann analysieren und entsprechend beantworten könnte.


Klingt dies nach der richtigen Richtung für die Implementierung eines solchen Systems, oder bin ich weit vom Kurs entfernt und / oder vermisse etwas ganz Offensichtliches?

Raben-Träumer
quelle
2
gamedev.stackexchange.com/questions/7718/… Dies ist eine zuvor gegebene Frage / Antwort, die Ihnen den Einstieg erleichtern kann.
James
@ James Ah - sieht aus wie ein guter Link. Vielen Dank! Ich glaube, ich habe das früher verpasst.
Raven Dreamer
4
Einige zusätzliche Hinweise: Entscheiden Sie, ob einige Ereignisse tatsächlich sofort statt am Ende des Frames wirksam werden sollen, insbesondere wenn ein Ereignishandler zusätzliche Ereignisse auslöst. Überlegen Sie, was passiert, wenn Sie Ereignisschleifen erhalten. Wenn Sie sich für eine Warteschlange entscheiden, benötigen Sie möglicherweise ein Prioritätssystem oder eine Möglichkeit, die Warteschlange zu umgehen.
Sam Hocevar

Antworten:

6

Dies kann so einfach sein, wie Sie möchten.

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

Versuchen Sie nicht herauszufinden, was ein Eventmanager tun sollte - finden Sie heraus, wozu Sie ihn benötigen. Der Rest sollte von dort folgen oder zumindest einige spezifischere Fragen vorschlagen.

Kylotan
quelle
3
+1 für "Versuche nicht herauszufinden, was ein X tun soll - finde heraus, wozu du es brauchst." Fügen Sie in der Tat erst dann einen Event-Manager hinzu, wenn Sie das Gefühl haben, eine entkoppelte und dennoch zentralisierte Methode zum Aufrufen von Funktionen zu benötigen.
@JoeWreschnig Oh Gott ja =) "Finde heraus, was du tun musst" Dies ist einer der drei wichtigsten Ratschläge, die sich jeder Entwickler zu Herzen nehmen sollte.
Patrick Hughes
Zuerst wollte ich Ihrem zweiten Satz nicht zustimmen, dass kein Event-Manager hinzugefügt werden soll, da die meisten Spiele von einer entkoppelten, aber zentralisierten Methode zum Aufrufen von Funktionen profitieren können, aber dann habe ich mir überlegt, wie viele kleine Spiele ich gemacht habe, die dies nicht getan haben habe jede Art von Eventmanager.
Also
3

Die drei wesentlichen Methoden, die ein Ereignissystem benötigt, sind eine addListener () -Methode, eine removeListener () -Methode und eine Methode zum dispatchEvent (). Das heißt, ein Methodenobjekt wird zum Registrieren für ein Ereignis verwendet, ein Methodenobjekt zum Aufheben der Registrierung und eine Methode zum tatsächlichen Senden eines Ereignisses an alle Listener. Alles andere ist Soße.

Wie Sie bemerken, benötigen Sie sicherlich eine Art Datenstruktur, um die registrierten Listener im Auge zu behalten. Der einfachste Ansatz wäre ein assoziatives Array (Wörterbuch oder ein Objekt in JavaScript), das einem Ereignis einen Vektor (oder einfach ein Array, abhängig von der Sprache) zuordnet. Dieser Vektor ist eine Liste aller registrierten Listener. Fügen Sie der Liste Listener hinzu oder entfernen Sie sie mit den Methoden add / removeListener ().

Das Senden eines Ereignisses in dispatchEvent () kann so einfach wie das Durchlaufen des Vektors sein. Sie können den Versand komplexer gestalten, indem Sie die Priorität von Ereignissen oder was auch immer sortieren. Machen Sie sich darüber jedoch keine Sorgen, bis Sie sie benötigen. In vielen Fällen brauchen Sie es nicht.

DispatchEvent () hat eine kleine Nuance, wenn Sie überlegen, welche Daten mit dem Ereignis weitergegeben werden sollen. Auf der einfachsten Ebene geben Sie möglicherweise keine zusätzlichen Daten weiter. Der Hörer muss nur wissen, dass ein Ereignis passiert ist. Bei den meisten Ereignissen sind jedoch zusätzliche Daten enthalten (z. B. wo das Ereignis aufgetreten ist), damit dispatchEvent () einige Parameter akzeptiert und weitergibt.

Ist es zum Beispiel wirklich notwendig, dass jedes meiner GameObjects ein "EventManagner" -Feld hat? Da alle meine GameObjects von einer einzelnen abstrakten übergeordneten Klasse erben, sollte ich in der Lage sein, eine statische Referenz zu verwenden, damit nur eine Instanz des EventManager von allen Spielobjekten gemeinsam genutzt wird.

Es gibt viele Möglichkeiten, allen Spielobjekten einen Verweis auf die EventManager-Klasse zu geben. Eine statische Referenz ist sicherlich eine Möglichkeit; Ein anderer ist mit einem Singleton. Beide Ansätze sind jedoch ziemlich unflexibel, sodass die meisten Leute hier entweder einen Service Locator oder eine Abhängigkeitsinjektion empfehlen. Ich habe eine Abhängigkeitsinjektion durchgeführt. Das bedeutet zwar ein separates EventManager-Feld für jedes Objekt, was Sie anscheinend vermeiden möchten, aber ich bin mir nicht sicher, warum dies ein Problem ist. Es ist nicht so, dass das Speichern einer Reihe von Zeigern viel Aufwand bedeutet.

jhocking
quelle
Ja, das erste Mal, dass ich die Abhängigkeitsinjektion wirklich verstanden und implementiert habe, war für ein EventDispatcher-Objekt. Sobald ich das unter meinen Gürtel bekommen habe, habe ich mich danach gesehnt, viele Sachen mit diesem Muster umzugestalten.
Schockieren
0

HAFTUNGSAUSSCHLUSS

Ich bin kein Java-Programmierer, aber ich habe einen Artikel geschrieben, in dem erklärt wird, wie ein Ereignissystem in C89, einer der einfachsten Sprachen (IMHO), erstellt wird

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

Die Grundlagen der Ereignisbehandlung sind ziemlich einfach.

  • Registrieren Sie ein Ereignis mit einem Rückruf
  • auslösendes Ereignis
  • Durchlaufen Sie die Listener, um festzustellen, ob eine Übereinstimmung vorliegt
  • Feuerwehrmann, falls gefunden
  • wiederholen

Wenn Sie dies wissen (aber nicht wissen, wie es in Java implementiert wird), ist es leicht zu verstehen, dass Sie einige Handler (Funktionen zum Behandeln der Ereignisse) benötigen und diese einem Array (mit Zeigern in C) hinzufügen, das mit einem Ereignisnamen gepaart ist. Sobald Sie ein Ereignis auslösen, speichern Sie den Namen des ausgelösten Ereignisses und seine Argumente in einem Stapel. Dies wird als Ereignispool bezeichnet.

Dann haben Sie einen Ereignis-Digest (eine einfache Schleife), der das erste Ereignis in diesem Stapel auslöst, versucht, einen Handler dafür zu finden, und ihn, falls vorhanden, mit den Parametern ausführt.

Natürlich kann diese Ereignisübersicht jederzeit ausgelöst werden, beispielsweise einmal pro Frame, nachdem Sie Ihre Update()Funktion aufgerufen haben .

PRDeving
quelle
-2

Verwenden Sie für das EventManager-Feld eine statische Variable. Ein Singleton für den EventManager wäre auch eine gute Idee.

Ihr Ansatz klingt gut, aber vergessen Sie nicht, ihn threadsicher zu machen.

Es ist gut, E / A-Ereignisse vor oder nach dem "GameEvent" auszuführen, sodass in einem Frame jedes "GameEvent" dieselben Daten verarbeitet.

Sibbo
quelle
"Thread-speichern"? Hmmm
U62
-1 für den völlig unnötigen und ungerechtfertigten Singleton-Vorschlag.