BEARBEITEN: Im Folgenden beschreibe ich ein grundlegendes Event-Messaging-System, das ich immer wieder verwendet habe. Und es ist mir aufgefallen, dass beide Schulprojekte Open Source und im Web sind. Sie können die zweite Version dieses Messagingsystems (und einiges mehr) bei http://sourceforge.net/projects/bpfat/ finden. Genießen Sie, und lesen Sie unten für eine mehr durch Beschreibung des Systems!
Ich habe ein generisches Nachrichtensystem geschrieben und es in eine Handvoll Spiele eingeführt, die auf der PSP sowie einige Unternehmensebene Anwendungssoftware veröffentlicht wurden. Der Sinn des Messagingsystems besteht darin, nur die Daten umzuleiten, die für die Verarbeitung einer Nachricht oder eines Ereignisses benötigt werden, je nachdem, welche Terminologie verwendet werden soll.
AA12
struct TEventMessage
{
int _iMessageID;
}
class IEventMessagingSystem
{
Post(int iMessageId);
Post(int iMessageId, float fData);
Post(int iMessageId, int iData);
// ...
Post(TMessageEvent * pMessage);
Post(int iMessageId, void * pData);
}
typedef float(*IEventMessagingSystem::Callback)(TEventMessage * pMessage);
class CEventMessagingSystem
{
Init ();
DNit ();
Exec (float fElapsedTime);
Post (TEventMessage * oMessage);
Register (int iMessageId, IEventMessagingSystem* pObject, FObjectCallback* fpMethod);
Unregister (int iMessageId, IEventMessagingSystem* pObject, FObjectCallback * fpMethod);
}
#define MSG_Startup (1)
#define MSG_Shutdown (2)
#define MSG_PlaySound (3)
#define MSG_HandlePlayerInput (4)
#define MSG_NetworkMessage (5)
#define MSG_PlayerDied (6)
#define MSG_BeginCombat (7)
#define MSG_EndCombat (8)
Und nun noch ein bisschen eine Erklärung: Schnelllauf nach unten verwendet der Liste der Objekte dieser etwas entlang der Linien von zu bewerkstelligen ist. Das erste Objekt, TEventMessage, ist das Basisobjekt zum Darstellen von Daten, die vom Nachrichtensystem gesendet werden. Standardmäßig wird immer die ID der Nachricht gesendet, wenn Sie sichergehen wollen, dass Sie eine Nachricht erhalten haben, die Sie erwartet haben (im Allgemeinen mache ich das nur im Debug).
Als nächstes folgt die Interface-Klasse, die ein generisches Objekt für das Messaging-System gibt, das beim Callback für das Casting verwendet wird. Darüber hinaus bietet es auch eine "einfach zu bedienende" Schnittstelle für das Versenden verschiedener Datentypen an das Messaging-System.
Danach haben wir unseren Callback typedef, Einfach gesagt erwartet es ein Objekt vom Typ der Interface-Klasse und wird einen TEventMessage-Zeiger übergeben ... Optional können Sie den Parameter const, aber ich habe Trickle-up-Verarbeitung vor für verwendet Dinge wie Stack-Debugging und solche des Messaging-Systems.
Zuletzt und im Kern ist das CEventMessagingSystem-Objekt. Dieses Objekt enthält ein Array von Callback-Objektstapeln (oder verknüpfte Listen oder Warteschlangen oder wie auch immer Sie die Daten speichern möchten). Die Callback-Objekte, die oben nicht gezeigt sind, müssen einen Zeiger auf das Objekt sowie die Methode zum Aufrufen dieses Objekts beibehalten (und sind eindeutig durch diese definiert). Wenn Sie Register() registrieren, fügen Sie einen Eintrag im Objektstapel unter der Array-Position der Nachrichten-ID hinzu. Wenn Sie die Registrierung aufheben() entfernen Sie diesen Eintrag.
Das ist es im Grunde .. Nun hat dies die Voraussetzung, dass alles über das IEventMessagingSystem und das TEventMessage-Objekt wissen muss ...aber dieses Objekt sollte sich nicht so oft ändern und nur die Teile von Informationen weitergeben, die für die Logik entscheidend sind, die von dem aufgerufenen Ereignis diktiert wird. Auf diese Weise muss ein Spieler nicht direkt über die Karte oder den Gegner Bescheid wissen, um Ereignisse an ihn zu senden. Ein verwaltetes Objekt kann eine API auch für ein größeres System aufrufen, ohne dass etwas darüber bekannt sein muss.
Zum Beispiel: Wenn ein Feind stirbt, möchten Sie, dass er einen Soundeffekt ausgibt. Angenommen, Sie haben einen Sound-Manager, der die IEventMessagingSystem-Schnittstelle erbt, würden Sie einen Rückruf für das Messaging-System einrichten, das einen TEventMessagePlaySoundEffect oder etwas Ähnliches akzeptiert. Der Sound Manager würde diesen Callback dann registrieren, wenn Soundeffekte aktiviert sind (oder die Registrierung aufheben, wenn Sie alle Soundeffekte für einfache Ein-/Aus-Funktionen deaktivieren möchten). Als nächstes würde das feindliche Objekt auch vom IEventMessaging-System erben, ein TEventMessagePlaySoundEffect-Objekt zusammensetzen (es müsste MSG_PlaySound für seine Message-ID und dann die ID des zu spielenden Soundeffekts sein, sei es eine int-ID oder der Name des Sounds Effekt) und rufen Sie einfach Post (& oEventMessagePlaySoundEffect).
Jetzt ist dies nur ein sehr einfaches Design ohne Implementierung. Wenn Sie sofort ausgeführt werden, müssen Sie die TEventemessage-Objekte (die ich hauptsächlich in Konsolenspielen verwendete) nicht puffern. Wenn Sie sich in einer Multithread-Umgebung befinden, ist dies eine sehr gut definierte Möglichkeit, dass Objekte und Systeme, die in separaten Threads ausgeführt werden, miteinander kommunizieren. Sie sollten jedoch die TEventemessage-Objekte beibehalten, damit die Daten bei der Verarbeitung verfügbar sind.
Eine weitere Änderung ist für Objekte, die nur Post() Daten benötigen, können Sie eine statische Gruppe von Methoden im IEventMessagingSystem erstellen, so dass sie nicht von ihnen erben müssen (Das ist für den einfachen Zugriff und Callback-Fähigkeiten verwendet) , nicht direkt für Post() -Aufrufe benötigt).
Für alle Leute, die MVC erwähnen, ist es ein sehr gutes Muster, aber Sie können es auf so viele verschiedene Arten und auf verschiedenen Ebenen implementieren. Das aktuelle Projekt, an dem ich beruflich arbeite, ist ein MVC-Setup etwa 3 mal, da gibt es den globalen MVC der gesamten Anwendung und dann ist designtechnisch jedes MV und C auch ein eigenständiges MVC-Pattern. Also was ich versucht habe zu tun Hier wird erklärt, wie man ein C erzeugt, das generisch genug ist, um fast jede Art von M zu handhaben, ohne dass man in eine Ansicht gelangen muss ...
Zum Beispiel ein Objekt, wenn es "stirbt" ein Soundeffekt. Sie würden eine Struktur für das Soundsystem wie TEventMessageSoundEffect erstellen, die von der TEventMessage erbt und eine Soundeffekt-ID hinzufügt (sei es ein vorgeladener Int oder der Name der SFX-Datei, wie auch immer sie in Ihrem System verfolgt werden). Dann muss das Objekt nur noch ein TEventMessageSoundEffect-Objekt mit dem entsprechenden Death-Noise-Objekt zusammensetzen und den Post (& oEventMessageSoundEffect); Objekt. Angenommen, der Ton ist nicht stummgeschaltet (was möchten Sie die Sound-Manager aufheben.
EDIT: Um dies ein wenig in Bezug auf den Kommentar unten zu verdeutlichen: Jedes Objekt zum Senden oder Empfangen einer Nachricht muss nur Ich kenne die IEventMessagingSystem-Schnittstelle, und dies ist das einzige Objekt, das das EventMessagingSystem von allen anderen Objekten wissen muss.Das gibt Ihnen die Trennung: Jedes Objekt, das eine Nachricht empfangen möchte, registriert einfach (MSG, Object, Callback) s Wenn dann ein Objekt Post (MSG, Data) aufruft, sendet es dieses über die ihm bekannte Schnittstelle an das EventMessagingSystem, der EMS wird dann jedes registrierte Objekt des Ereignisses benachrichtigen.Sie könnten ein MSG_PlayerDied ausführen, das andere Systeme behandeln, oder das Der Player kann MSG_PlaySound, MSG_Respawn usw. aufrufen, damit die Nachrichten auf diese Nachrichten reagieren können Post (MSG, Data) als abstrahierte API zu den verschiedenen Systemen innerhalb einer Game Engine.
Oh! Eine andere Sache, auf die ich hingewiesen wurde. Das System, das ich oben beschrieben habe, passt in das Observer-Muster in der anderen gegebenen Antwort. Wenn Sie also eine allgemeinere Beschreibung wollen, um mir etwas mehr Sinn zu geben, ist das ein kurzer Artikel, der eine gute Beschreibung gibt.
Hoffe das hilft und viel Spaß!
+1 für die gründliche Erklärung, aber ich habe auch eine Bemerkung: Sie sagten, dass * ein Spieler muss nicht über die Karte wissen *, um Ereignisse zu senden, aber Ihr Beispiel impliziert, dass ein sterbender Feind über jeden anderen wissen muss Teil des Programms, das benachrichtigt werden muss. Ich hätte erwartet, dass es einfach eine "Ich bin gerade gestorben" -Meldung senden würde, und dann würde Ihr Nachrichtensystem die Zuhörer informieren, die an diesem Ereignis interessiert sind (Ton abspielen, Punkte aktualisieren, usw.). Auf diese Weise sieht es so aus, als ob jede Entität eine Menge Nachrichten für ein einzelnes Ereignis senden muss (Ton abspielen, Punktzahl erhöhen). Oder habe ich es falsch verstanden? – Groo
@Groo Ich konnte meine Antwort nicht genug verkürzen, also habe ich sie in meine Antwort oben bearbeitet. – James
Hallo Mann, es ist über 5 Jahre her seit Ihrer Antwort, aber dieser Post ist gekommen, als ich nach einer einfachen pubsub Idee suchte, und ich muss sagen, dass ich die Quellen heruntergeladen habe, und abgesehen von den Kodierungsstandards Ich bin nicht daran gewöhnt und die Tatsache, dass C++ seit 2005 ein wenig fortgeschritten ist, ist sehr interessant für die Forschung und ich habe einige von EMS Skelett für mein C# Spiel verwendet. Es sieht wirklich erstaunlich und schwierig aus, was ihr drei Jungs gemacht habt, und ich hoffe, ich würde mehr davon lernen! –