Wie kann die Nachrichtenbehandlung in einem komponentenbasierten Entitätssystem ordnungsgemäß implementiert werden?


30

Ich implementiere eine Entitätssystemvariante mit:

  • Eine Entitätsklasse , die kaum mehr als eine ID ist, die Komponenten miteinander verbindet

  • Eine Reihe von Komponentenklassen , die keine "Komponentenlogik", sondern nur Daten enthalten

  • Eine Reihe von Systemklassen (auch bekannt als "Subsysteme", "Manager"). Diese erledigen die gesamte Verarbeitung der Entitätslogik. In den meisten einfachen Fällen iterieren die Systeme nur durch eine Liste von Entitäten, an denen sie interessiert sind, und führen für jede eine Aktion aus

  • Ein MessageChannel-Klassenobjekt , das von allen Spielsystemen gemeinsam genutzt wird. Jedes System kann bestimmte Arten von Nachrichten abonnieren, die abgehört werden sollen, und über den Kanal Nachrichten an andere Systeme senden

Die ursprüngliche Variante der Behandlung von Systemnachrichten war ungefähr so:

  1. Führen Sie nacheinander ein Update für jedes Spielsystem aus
  2. Wenn ein System eine Komponente bearbeitet und diese Aktion für andere Systeme von Interesse sein könnte, sendet das System eine entsprechende Nachricht (z. B. einen Systemaufruf)

    messageChannel.Broadcast(new EntityMovedMessage(entity, oldPosition, newPosition))

    wann immer ein Objekt bewegt wird)

  3. Jedes System, das die bestimmte Nachricht abonniert hat, erhält die aufgerufene Nachrichtenbehandlungsmethode

  4. Wenn ein System ein Ereignis verarbeitet und die Ereignisverarbeitungslogik das Senden einer anderen Nachricht erfordert, wird die Nachricht sofort gesendet und eine andere Kette von Nachrichtenverarbeitungsmethoden wird aufgerufen

Diese Variante war in Ordnung, bis ich anfing, das Kollisionserkennungssystem zu optimieren (es wurde sehr langsam, als die Anzahl der Entitäten zunahm). Zuerst würde es einfach jedes Entitätspaar unter Verwendung eines einfachen Brute-Force-Algorithmus iterieren. Dann habe ich einen "räumlichen Index" hinzugefügt, der ein Gitter von Zellen enthält, in dem Entitäten gespeichert sind, die sich im Bereich einer bestimmten Zelle befinden, sodass nur Entitäten in benachbarten Zellen überprüft werden können.

Jedes Mal, wenn sich eine Entität bewegt, prüft das Kollisionssystem, ob die Entität mit etwas an der neuen Position kollidiert. Ist dies der Fall, wird eine Kollision erkannt. Und wenn beide kollidierenden Entitäten "physische Objekte" sind (beide haben die RigidBody-Komponente und sollen sich gegenseitig wegdrücken, um nicht den gleichen Raum einzunehmen), fordert ein spezielles System zur Trennung starrer Körper das Bewegungssystem auf, die Entitäten zu einigen zu bewegen spezifische Positionen, die sie trennen würden. Dies wiederum veranlasst das Bewegungssystem, Nachrichten zu senden, die über geänderte Entitätspositionen informieren. Das Kollisionserkennungssystem soll reagieren, da es seinen räumlichen Index aktualisieren muss.

In einigen Fällen tritt ein Problem auf, weil der Inhalt der Zelle (eine generische Liste von Entitätsobjekten in C #) geändert wird, während sie durchlaufen werden, wodurch der Iterator eine Ausnahme auslöst.

Also ... wie kann ich verhindern, dass das Kollisionssystem unterbrochen wird, während es nach Kollisionen sucht?

Natürlich könnte ich eine "clevere" / "knifflige" Logik hinzufügen, die sicherstellt, dass der Zelleninhalt korrekt durchlaufen wird, aber ich denke, das Problem liegt nicht im Kollisionssystem selbst (ich hatte auch ähnliche Probleme in anderen Systemen), sondern in der Art und Weise Nachrichten werden auf dem Weg von System zu System behandelt. Was ich brauche, ist eine Möglichkeit, um sicherzustellen, dass eine bestimmte Ereignisbehandlungsmethode ihren Job ohne Unterbrechungen erledigt.

Was ich ausprobiert habe:

  • Warteschlangen für eingehende Nachrichten . Jedes Mal, wenn ein System eine Nachricht rundsendet, wird die Nachricht zu den Nachrichtenwarteschlangen von Systemen hinzugefügt, die daran interessiert sind. Diese Nachrichten werden verarbeitet, wenn ein Systemupdate für jeden Frame aufgerufen wird. Das Problem : Wenn ein System A eine Nachricht zur Warteschlange von System B hinzufügt, funktioniert es gut, wenn System B später als System A (im selben Spiel-Frame) aktualisiert werden soll. Andernfalls verarbeitet die Nachricht den nächsten Spielrahmen (für einige Systeme nicht wünschenswert).
  • Warteschlangen für ausgehende Nachrichten . Während ein System ein Ereignis verarbeitet, werden alle von ihm gesendeten Nachrichten zur Warteschlange für ausgehende Nachrichten hinzugefügt. Die Nachrichten müssen nicht auf die Verarbeitung eines Systemupdates warten: Sie werden "sofort" behandelt, nachdem der erste Nachrichten-Handler seine Arbeit beendet hat. Wenn die Verarbeitung der Nachrichten dazu führt, dass andere Nachrichten gesendet werden, werden auch diese einer ausgehenden Warteschlange hinzugefügt, sodass alle Nachrichten im selben Frame verarbeitet werden. Das Problem: Wenn ein Entity-Lifetime-System (ich habe das Entity-Lifetime-Management mit einem System implementiert) eine Entität erstellt, werden einige Systeme A und B darüber benachrichtigt. Während System A die Nachricht verarbeitet, verursacht es eine Kette von Nachrichten, die schließlich zur Zerstörung der erstellten Entität führen (z. B. wurde eine Aufzählungsentität genau dort erstellt, wo sie mit einem Hindernis kollidiert, wodurch die Aufzählungspunkte selbst zerstört werden). Während die Nachrichtenkette aufgelöst wird, ruft System B die Entitätserstellungsnachricht nicht ab. Wenn also System B auch an der Entitätszerstörungsnachricht interessiert ist, erhält es diese und erst, nachdem die "Kette" aufgelöst wurde, erhält es die ursprüngliche Entitätserzeugungsnachricht. Dadurch wird die Zerstörungsnachricht ignoriert und die Erstellungsnachricht "akzeptiert".

BEARBEITEN - ANTWORTEN AUF FRAGEN, KOMMENTARE:

  • Wer ändert den Inhalt der Zelle, während das Kollisionssystem darüber iteriert?

Während das Kollisionssystem Kollisionsprüfungen für eine Entität und deren Nachbarn durchführt, wird möglicherweise eine Kollision erkannt und das Entitätssystem sendet eine Nachricht, auf die andere Systeme sofort reagieren. Die Reaktion auf die Nachricht kann dazu führen, dass andere Nachrichten erstellt und sofort verarbeitet werden. So könnte ein anderes System eine Meldung erstellen, die das Kollisionssystem dann sofort verarbeiten müsste (z. B. wenn eine Entität verschoben wurde und das Kollisionssystem ihren räumlichen Index aktualisieren muss), obwohl die früheren Kollisionsprüfungen noch nicht abgeschlossen waren.

  • Können Sie nicht mit einer globalen Warteschlange für ausgehende Nachrichten arbeiten?

Ich habe kürzlich eine einzelne globale Warteschlange ausprobiert. Es verursacht neue Probleme. Problem: Ich verschiebe ein Panzerelement in ein Wandelement (der Panzer wird mit der Tastatur gesteuert). Dann entscheide ich mich, die Richtung des Panzers zu ändern. Um den Tank und die Wand von jedem Rahmen zu trennen, bewegt das CollidingRigidBodySeparationSystem den Tank so weit wie möglich von der Wand weg. Die Trennrichtung sollte der Bewegungsrichtung des Panzers entgegengesetzt sein (wenn das Spiel beginnt, sollte der Panzer so aussehen, als würde er sich niemals in die Wand bewegen). Die Richtung wird jedoch entgegengesetzt zur NEUEN Richtung, wodurch der Tank auf eine andere Seite der Wand bewegt wird als ursprünglich. Warum das Problem auftritt: So werden Nachrichten jetzt behandelt (vereinfachter Code):

public void Update(int deltaTime)
{   
    m_messageQueue.Enqueue(new TimePassedMessage(deltaTime));
    while (m_messageQueue.Count > 0)
    {
        Message message = m_messageQueue.Dequeue();
        this.Broadcast(message);
    }
}

private void Broadcast(Message message)
{       
    if (m_messageListenersByMessageType.ContainsKey(message.GetType()))
    {
        // NOTE: all IMessageListener objects here are systems.
        List<IMessageListener> messageListeners = m_messageListenersByMessageType[message.GetType()];
        foreach (IMessageListener listener in messageListeners)
        {
            listener.ReceiveMessage(message);
        }
    }
}

Der Code fließt wie folgt (nehmen wir an, es ist nicht das erste Spielfeld):

  1. Die Systeme beginnen mit der Verarbeitung von TimePassedMessage
  2. InputHandingSystem wandelt Tastendrücke in Entitätsaktionen um (in diesem Fall wird aus einem Pfeil nach links eine MoveWest-Aktion). Die Entitätsaktion wird in der ActionExecutor-Komponente gespeichert
  3. ActionExecutionSystem fügt als Reaktion auf die Entitätsaktion eine MovementDirectionChangeRequestedMessage am Ende der Nachrichtenwarteschlange hinzu
  4. MovementSystem verschiebt die Objektposition basierend auf den Velocity-Komponentendaten und fügt die PositionChangedMessage-Nachricht am Ende der Warteschlange hinzu. Die Bewegung erfolgt mit der Bewegungsrichtung / Geschwindigkeit des vorherigen Frames (sagen wir Norden)
  5. Systeme beenden die Verarbeitung von TimePassedMessage
  6. Die Systeme beginnen mit der Verarbeitung von MovementDirectionChangeRequestedMessage
  7. MovementSystem ändert die Geschwindigkeit / Bewegungsrichtung des Objekts wie gewünscht
  8. Systeme beenden die Verarbeitung von MovementDirectionChangeRequestedMessage
  9. Die Systeme beginnen mit der Verarbeitung von PositionChangedMessage
  10. CollisionDetectionSystem erkennt, dass eine Entität, die sich bewegt hat, in eine andere Entität geraten ist (Tank ist in eine Wand gefahren). Es wird eine CollisionOccuredMessage zur Warteschlange hinzugefügt
  11. Systeme stoppen die Verarbeitung von PositionChangedMessage
  12. Die Systeme beginnen mit der Verarbeitung von CollisionOccuredMessage
  13. CollidingRigidBodySeparationSystem reagiert auf Kollisionen durch Trennung von Tank und Wand. Da die Wand statisch ist, wird nur der Tank bewegt. Die Bewegungsrichtung der Panzer wird als Indikator dafür verwendet, woher der Panzer kam. Es ist in entgegengesetzter Richtung versetzt

BUG: Als der Panzer diesen Rahmen bewegte, bewegte er sich mit der Bewegungsrichtung des vorherigen Rahmens, aber als er getrennt wurde, wurde die Bewegungsrichtung von DIESEM Rahmen verwendet, obwohl sie bereits anders war. So sollte es nicht funktionieren!

Um diesen Fehler zu vermeiden, muss die alte Bewegungsrichtung irgendwo gespeichert werden. Ich könnte es zu einer Komponente hinzufügen, nur um diesen speziellen Fehler zu beheben, aber deutet dieser Fall nicht auf eine grundlegend falsche Art des Umgangs mit Nachrichten hin? Warum sollte das Trennsystem darauf achten, welche Bewegungsrichtung es verwendet? Wie kann ich dieses Problem elegant lösen?

  • Vielleicht möchten Sie gamadu.com/artemis lesen, um zu sehen, was sie mit Aspects gemacht haben. Auf welcher Seite treten einige der Probleme auf, die Sie sehen.

Eigentlich kenne ich Artemis schon eine ganze Weile. Untersuchte den Quellcode, las die Foren usw. Aber ich habe gesehen, dass "Aspekte" nur an wenigen Stellen erwähnt wurden und sie bedeuten, soweit ich das verstehe, im Grunde "Systeme". Aber ich kann nicht sehen, wie Artemis einige meiner Probleme angeht. Es werden nicht einmal Nachrichten verwendet.

  • Siehe auch: "Entitätskommunikation: Nachrichtenwarteschlange vs Publish / Subscribe vs Signal / Slots"

Ich habe bereits alle Fragen zu gamedev.stackexchange in Bezug auf Entity-Systeme gelesen. Dieser scheint die Probleme, mit denen ich konfrontiert bin, nicht zu diskutieren. Vermisse ich etwas?

  • Behandeln Sie die beiden Fälle unterschiedlich. Die Aktualisierung des Rasters muss nicht auf den Bewegungsnachrichten basieren, da diese Teil des Kollisionssystems sind

Ich bin mir nicht sicher was du meinst. Ältere Implementierungen von CollisionDetectionSystem prüften nur auf Kollisionen bei einem Update (wenn eine TimePassedMessage verarbeitet wurde), aber ich musste die Überprüfungen aufgrund der Leistung so gering wie möglich halten. Deshalb habe ich auf Kollisionsprüfung umgestellt, wenn sich eine Entität bewegt (die meisten Entitäten in meinem Spiel sind statisch).


Es gibt etwas, das mir nicht klar ist. Wer ändert den Inhalt der Zelle, während das Kollisionssystem darüber iteriert?
Paul Manta

Können Sie nicht mit einer globalen Warteschlange für ausgehende Nachrichten arbeiten? Alle darin enthaltenen Nachrichten werden also jedes Mal gesendet, wenn ein System fertig ist. Dies schließt die Selbstzerstörung des Systems ein.
Roy T.

Wenn Sie dieses gewundene Design beibehalten möchten, müssen Sie @RoyT folgen. Dies ist der einzige Weg (ohne komplexe, zeitbasierte Nachrichtenübermittlung), um Ihr Sequenzierungsproblem zu lösen. Vielleicht möchten Sie gamadu.com/artemis lesen, um zu sehen, was sie mit Aspects gemacht haben. Auf welcher Seite treten einige der Probleme auf, die Sie sehen.
Patrick Hughes


2
Möglicherweise möchten Sie erfahren, wie Axum dies getan hat, indem Sie das CTP heruntergeladen und Code kompiliert haben. Anschließend können Sie das Ergebnis mit ILSpy in C # zurückentwickeln. Die Weitergabe von Nachrichten ist ein wichtiges Merkmal der Darstellermodellsprachen, und ich bin mir sicher, dass Microsoft weiß, was sie tun. Sie haben also möglicherweise die 'beste' Implementierung.
Jonathan Dickinson

Antworten:


12

Sie haben wahrscheinlich von dem Anti-Muster des God / Blob-Objekts gehört. Nun, Ihr Problem ist eine God / Blob-Schleife. Das Basteln an Ihrem Nachrichtenübermittlungssystem ist bestenfalls eine Band-Aid-Lösung und im schlimmsten Fall eine reine Zeitverschwendung. Tatsächlich hat Ihr Problem überhaupt nichts spezielles mit der Spieleentwicklung zu tun. Ich habe mich dabei erwischt, wie ich versucht habe, eine Sammlung zu ändern, während ich sie mehrmals durchlaufen habe, und die Lösung ist immer dieselbe: Unterteilen, Unterteilen, Unterteilen.

So wie ich den Wortlaut Ihrer Frage verstehe, sieht Ihre Methode zur Aktualisierung Ihres Kollisionssystems derzeit im Großen und Ganzen wie folgt aus.

for each possible collision
    check for collision
    handle collision
    modify collision world to reflect change // exception happens here

So einfach geschrieben können Sie sehen, dass Ihre Schleife drei Verantwortlichkeiten hat, wenn sie nur eine haben sollte. Um Ihr Problem zu lösen, teilen Sie Ihre aktuelle Schleife in drei separate Schleifen auf, die drei verschiedene algorithmische Durchläufe darstellen .

for each possible collision
    check for collision, record it if a collision occurs

for each found collision
    handle collision, record the collision response (delete object, ignore, etc.)

for each collision response
    modify collision world according to response

Indem Sie Ihre ursprüngliche Schleife in drei Teilschleifen unterteilen, versuchen Sie nie mehr, die Sammlung zu ändern, über die Sie gerade iterieren. Beachten Sie auch, dass Sie nicht mehr als in Ihrer ursprünglichen Schleife arbeiten und dass Sie möglicherweise einige Cache-Gewinne erzielen, wenn Sie dieselben Vorgänge mehrere Male hintereinander ausführen.

Ein weiterer Vorteil ist, dass Sie jetzt Parallelität in Ihren Code einführen können. Ihr Ansatz mit kombinierten Schleifen ist von Natur aus seriell (was im Grunde genommen die Ausnahme für gleichzeitige Änderungen ist!), Da jede Schleifeniteration möglicherweise sowohl Lese- als auch Schreibvorgänge in Ihre Kollisionswelt ausführt. Die drei oben dargestellten Teilschleifen sind jedoch alle entweder Lese- oder Schreibschleifen, jedoch nicht beide. Zumindest der erste Durchgang, bei dem alle möglichen Kollisionen überprüft werden, ist peinlich parallel geworden, und je nachdem, wie Sie Ihren Code schreiben, können auch der zweite und der dritte Durchgang durchgeführt werden.


Dem stimme ich voll und ganz zu. Ich benutze diesen sehr ähnlichen Ansatz in meinem Spiel und ich glaube, dass sich dies auf lange Sicht auszahlen wird. So sollte das Kollisionssystem (oder der Kollisionsmanager) funktionieren (ich glaube tatsächlich, dass es möglich ist, überhaupt kein Nachrichtensystem zu haben).
Emiliano

11

Wie kann die Nachrichtenbehandlung in einem komponentenbasierten Entitätssystem ordnungsgemäß implementiert werden?

Ich würde sagen, dass Sie zwei Arten von Nachrichten möchten: Synchron und Asynchron. Synchrone Nachrichten werden sofort verarbeitet, während asynchrone Nachrichten nicht im selben Stack-Frame verarbeitet werden (sondern möglicherweise im selben Game-Frame). Die Entscheidung, was ist, wird normalerweise auf der Basis "pro Nachrichtenklasse" getroffen, z. B. "alle EnemyDied- Nachrichten sind asynchron".

Einige Ereignisse werden mit einer dieser Methoden sehr viel einfacher gehandhabt . Meiner Erfahrung nach ist ein ObjectGetsDeletedNow - Ereignis viel weniger sexy und Rückrufe sind viel schwieriger zu implementieren als ObjectWillBeDeletedAtEndOfFrame. Andererseits wird jeder "Veto" -ähnliche Message-Handler (Code, der bestimmte Aktionen abbrechen oder ändern kann, während sie ausgeführt werden, wie ein Shield-Effekt das DamageEvent modifiziert ) in asynchronen Umgebungen nicht einfach, sondern ein Kinderspiel synchrone Anrufe.

In einigen Fällen kann die asynchrone Verarbeitung effizienter sein (z. B. können Sie einige Ereignishandler überspringen, wenn das Objekt ohnehin später gelöscht wird). Manchmal ist die Synchronisierung effizienter, insbesondere wenn das Berechnen des Parameters für ein Ereignis kostspielig ist und Sie lieber Callback-Funktionen zum Abrufen bestimmter Parameter anstelle bereits berechneter Werte übergeben möchten (falls sich ohnehin niemand für diesen bestimmten Parameter interessiert).

Sie haben bereits ein weiteres allgemeines Problem bei Nur-Synchron-Nachrichtensystemen erwähnt: Nach meiner Erfahrung mit synchronen Nachrichtensystemen ist eine der häufigsten Ursachen für Fehler und Trauer im Allgemeinen das Ändern von Listen beim Durchlaufen dieser Listen.

Denken Sie darüber nach: Es liegt in der Natur des synchronen (sofortige Behandlung aller Nachwirkungen einer Aktion) und des Nachrichtensystems (Entkoppeln des Empfängers vom Absender, sodass der Absender nicht weiß, wer auf Aktionen reagiert), dass Sie nicht in der Lage sind, auf einfache Weise zu reagieren finde solche Schleifen. Was ich sage ist: Seien Sie darauf vorbereitet, mit dieser Art von sich selbst modifizierender Iteration viel umzugehen. Seine Art von "by Design". ;-)

Wie kann ich verhindern, dass das Kollisionssystem während der Kollisionsprüfung unterbrochen wird?

Für Ihr spezielles Problem mit der Kollisionserkennung ist es möglicherweise ausreichend, Kollisionsereignisse asynchron zu machen, sodass sie in die Warteschlange gestellt werden, bis der Kollisionsmanager abgeschlossen ist und anschließend als ein Stapel ausgeführt wird (oder zu einem späteren Zeitpunkt im Frame). Dies ist Ihre Lösung "Incoming Queue".

Das Problem: Wenn ein System A eine Nachricht zur Warteschlange von System B hinzufügt, funktioniert es gut, wenn System B später als System A (im selben Spiel-Frame) aktualisiert werden soll. Andernfalls verarbeitet die Nachricht den nächsten Spielrahmen (für einige Systeme nicht wünschenswert).

Einfach:

while (! queue.empty ()) {queue.pop (). handle (); }

Führen Sie die Warteschlange einfach immer wieder aus, bis keine Nachricht mehr vorhanden ist. (Wenn Sie jetzt "Endlosschleife" schreien, denken Sie daran, dass Sie höchstwahrscheinlich dieses Problem als "Nachrichten-Spamming" haben würden, wenn es auf den nächsten Frame verzögert würde. Sie können () für eine vernünftige Anzahl von Iterationen angeben, um Endlosschleifen zu erkennen. wenn du Lust hast;))


Beachten Sie, dass ich nicht genau darüber gesprochen habe, wann asynchrone Nachrichten verarbeitet werden. Meiner Meinung nach ist es vollkommen in Ordnung, dem Kollisionserkennungsmodul zu ermöglichen, seine Nachrichten nach Beendigung zu löschen. Sie können sich dies auch als "synchrone Nachrichten, die bis zum Ende der Schleife verzögert sind" oder als eine raffinierte Methode vorstellen, "die Iteration nur so zu implementieren, dass sie während des Iterierens geändert werden kann"
Imi

5

Wenn Sie tatsächlich versuchen, die datenorientierte Gestaltung von ECS zu nutzen, sollten Sie sich überlegen, wie Sie am besten mit DOD vorgehen können.

Schauen Sie sich den BitSquid-Blog an , insbesondere den Teil über Ereignisse. Ein System, das sich gut in ECS einfügt, wird vorgestellt. Puffern Sie alle Ereignisse in eine schöne, saubere Warteschlange vom Typ "pro Nachricht", so wie Systeme in einem ECS pro Komponente sind. Später aktualisierte Systeme können die Warteschlange für einen bestimmten Nachrichtentyp effizient durchlaufen, um sie zu verarbeiten. Oder ignoriere sie einfach. Welcher auch immer.

Beispielsweise würde das CollisionSystem einen Puffer voller Kollisionsereignisse generieren. Jedes andere System, das nach einer Kollision ausgeführt wird, kann dann die Liste durchlaufen und diese nach Bedarf verarbeiten.

Es behält die datenorientierte Parallelität des ECS-Entwurfs bei, ohne die Komplexität der Nachrichtenregistrierung oder dergleichen. Nur Systeme, die sich tatsächlich für einen bestimmten Ereignistyp interessieren, durchlaufen die Warteschlange für diesen Typ und führen eine direkte Iteration in einem Durchgang durch die Nachrichtenwarteschlange aus, um den bestmöglichen Wirkungsgrad zu erzielen.

Wenn Sie die Komponenten in jedem System konsistent geordnet lassen (z. B. alle Komponenten nach Entitäts-ID oder ähnlichem ordnen), erhalten Sie sogar den Vorteil, dass Nachrichten in der effizientesten Reihenfolge generiert werden, um sie zu durchlaufen und die entsprechenden Komponenten im zu suchen Verarbeitungssystem. Wenn Sie also die Entitäten 1, 2 und 3 haben, werden die Nachrichten in dieser Reihenfolge generiert, und die während der Verarbeitung der Nachricht durchgeführten Komponentensuchen werden in einer streng aufsteigenden Adressreihenfolge (die am schnellsten ist) ausgeführt.


1
+1, aber ich kann nicht glauben, dass dieser Ansatz keine Nachteile hat. Zwingt uns dies nicht, Abhängigkeiten zwischen Systemen fest zu codieren? Oder sind diese Abhängigkeiten auf die eine oder andere Weise fest codiert?
Patryk Czachurski

2
@Daedalus: wenn Spiellogik Physik - Updates muss die richtige Logik tun, wie werden Sie nicht gehen diese Abhängigkeit haben? Selbst mit einem Pubsub-Modell müssen Sie explizit den einen oder anderen Nachrichtentyp abonnieren, der nur von einem anderen System generiert wird. Das Vermeiden von Abhängigkeiten ist schwierig und es geht meist nur darum, die richtigen Ebenen zu finden. Grafik und Physik sind zum Beispiel unabhängig, aber es wird eine übergeordnete Klebeschicht geben, die sicherstellt, dass interpolierte Aktualisierungen der Physiksimulation in Grafiken usw. wiedergegeben werden.
Sean Middleditch

Dies sollte die akzeptierte Antwort sein. Eine einfache Möglichkeit besteht darin, eine neue Art von Komponente zu erstellen, z. B. CollisionResolvable, die von jedem System verarbeitet wird, das nach einer Kollision etwas unternehmen möchte. Das würde gut zu Drakes Vorschlag passen, es gibt jedoch ein System für jede Unterteilungsschleife.
User8363
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.