Ich mache ein einfaches Spiel und habe beschlossen, ein Messaging-System zu implementieren.
Das System sieht im Grunde so aus:
Entität generiert Nachricht -> Nachricht wird in die globale Nachrichtenwarteschlange gestellt -> messageManager benachrichtigt jedes Objekt über die neue Nachricht über onMessageReceived (Nachrichtennachricht) -> wenn das Objekt dies wünscht, wirkt es auf die Nachricht.
Die Art und Weise, wie ich Nachrichtenobjekte erstelle, ist wie folgt:
//base message class, never actually instantiated
abstract class Message{
Entity sender;
}
PlayerDiedMessage extends Message{
int livesLeft;
}
Jetzt kann meine SoundManagerEntity in ihrer onMessageReceived () -Methode so etwas tun
public void messageReceived(Message msg){
if(msg instanceof PlayerDiedMessage){
PlayerDiedMessage diedMessage = (PlayerDiedMessage) msg;
if(diedMessage.livesLeft == 0)
playSound(SOUND_DEATH);
}
}
Die Profis dieses Ansatzes:
- Sehr einfach und leicht zu implementieren
- Die Nachricht kann so viele Informationen enthalten, wie Sie möchten, da Sie einfach eine neue Nachrichtenunterklasse erstellen können, die alle erforderlichen Informationen enthält.
Die Nachteile:
- Ich kann nicht herausfinden, wie ich Nachrichtenobjekte in einen Objektpool recyceln kann , es sei denn, ich habe für jede Unterklasse von Nachrichten einen anderen Pool. Ich habe also im Laufe der Zeit sehr viel Objekterstellung / Speicherzuweisung.
- Ich kann keine Nachricht an einen bestimmten Empfänger senden, aber das habe ich in meinem Spiel noch nicht gebraucht, daher macht es mir nichts aus.
Was fehlt mir hier? Es muss eine bessere Implementierung oder eine Idee geben, die mir fehlt.
architecture
messaging
object-pools
you786
quelle
quelle
Antworten:
Eine einfache Antwort ist, dass Sie keine Nachrichten recyceln möchten. Man recycelt Objekte, die entweder zu Tausenden in jedem Frame erstellt (und entfernt) werden, wie Partikel, oder sehr schwer sind (Dutzende von Eigenschaften, langer Initialisierungsprozess).
Wenn Sie ein Schneepartikel mit einer Bitmap, Geschwindigkeiten und physikalischen Attributen haben, die gerade unter Ihrer Ansicht liegen, möchten Sie es möglicherweise recyceln, anstatt ein neues Partikel zu erstellen und die Bitmap zu initialisieren und die Attribute erneut zu randomisieren. In Ihrem Beispiel enthält die Nachricht nichts Nützliches, um sie beizubehalten, und Sie verschwenden mehr Ressourcen für das Entfernen der instanzspezifischen Eigenschaften als für das Erstellen eines neuen Nachrichtenobjekts.
quelle
In meinem Java-basierten Spiel habe ich eine sehr ähnliche Lösung gefunden, außer dass mein Code für die Nachrichtenverarbeitung so aussieht:
Ich benutze Anmerkungen, um eine Methode als Ereignishandler zu markieren. Dann verwende ich beim Spielstart Reflection, um eine Zuordnung (oder, wie ich es nenne, "Piping") von Nachrichten zu berechnen - welche Methode für welches Objekt aufgerufen werden soll, wenn ein Ereignis einer bestimmten Klasse gesendet wird. Das funktioniert ... großartig.
Die Rohrleitungsberechnung kann etwas kompliziert werden, wenn Sie dem Mix Schnittstellen und Unterklassen hinzufügen möchten, ist aber dennoch recht einfach. Während des Spielladens kommt es zu einer leichten Verlangsamung, wenn alle Klassen gescannt werden müssen, dies geschieht jedoch nur einmal (und kann wahrscheinlich in einen separaten Thread verschoben werden). Stattdessen ist das tatsächliche Senden von Nachrichten billiger - ich muss nicht jede "messageReceived" -Methode in der Codebasis aufrufen, um zu überprüfen, ob das Ereignis von einer guten Klasse ist.
quelle
EDIT Liosans Antwort ist sexier. Schau in Es hinein.
Wie Markus sagte, sind Nachrichten kein häufiger Kandidat für Objektpools. Kümmere dich nicht darum aus den Gründen, die er erwähnt hat. Wenn Ihr Spiel Unmengen von Nachrichten bestimmter Typen sendet, lohnt es sich vielleicht. Aber an diesem Punkt würde ich vorschlagen, dass Sie zu direkten Methodenaufrufen wechseln.
Apropos,
Wenn Sie einen bestimmten Empfänger kennen, an den Sie ihn senden möchten, wäre es dann nicht ziemlich einfach, einen Verweis auf dieses Objekt zu erhalten und einen direkten Methodenaufruf darauf durchzuführen? Sie können auch bestimmte Objekte hinter Manager-Klassen ausblenden, um den systemübergreifenden Zugriff zu erleichtern.
Ihre Implementierung hat einen Nachteil, und es besteht das Potenzial, dass viele Objekte Nachrichten erhalten, die ihnen nicht wirklich wichtig sind. Im Idealfall erhalten Ihre Klassen nur Nachrichten, die sie wirklich interessieren.
Mit a können Sie
HashMap<Class, LinkedList<Messagable>>
Nachrichtentypen einer Liste von Objekten zuordnen, die diesen Nachrichtentyp empfangen möchten.Das Abonnieren eines Nachrichtentyps würde ungefähr so aussehen:
Sie könnten dies so implementieren
subscribe
(verzeihen Sie mir einige Fehler, ich habe seit ein paar Jahren kein Java mehr geschrieben):Und dann könnten Sie eine Nachricht wie folgt senden:
Und
broadcastMessage
könnte so implementiert werden:Sie können Nachrichten auch so abonnieren, dass Sie Ihre Nachrichtenverarbeitung in verschiedene anonyme Funktionen aufteilen können:
Es ist ein wenig ausführlich, hilft aber bei der Organisation. Sie können auch eine zweite Funktion implementieren
subscribeAll
, die eine separate Liste vonMessagable
s enthält, die über alles hören möchten.quelle
1 . Tu es nicht.
Nachrichten sind kostengünstig. Ich stimme Markus von Broady zu. GC wird sie schnell sammeln. Versuchen Sie, nicht viele zusätzliche Informationen für Nachrichten anzuhängen. Sie sind nur Nachrichten. Das Finden einer Instanz von ist eine Information für sich. Verwenden Sie es (wie in Ihrem Beispiel).
2 . Sie können versuchen, MessageDispatcher zu verwenden .
Im Gegensatz zur ersten Lösung könnten Sie einen zentralen Nachrichten-Dispatcher haben, der Nachrichten verarbeitet und an beabsichtigte Objekte weiterleitet.
quelle