Bisher haben die von mir verwendeten Entity-Komponentensysteme hauptsächlich wie Javas Artemis funktioniert:
- Alle Daten in Komponenten
- Zustandslose unabhängige Systeme (zumindest in dem Maße, in dem sie bei der Initialisierung keine Eingabe erfordern) iterieren über jede Entität, die nur die Komponenten enthält, an denen ein bestimmtes System interessiert ist
- Alle Systeme verarbeiten ihre Entitäten mit einem Tick, dann beginnt das Ganze von vorne.
Jetzt versuche ich, dies zum ersten Mal auf ein rundenbasiertes Spiel anzuwenden, mit Tonnen von Ereignissen und Reaktionen, die in einer festgelegten Reihenfolge relativ zueinander auftreten müssen, bevor das Spiel fortgesetzt werden kann. Ein Beispiel:
Spieler A erhält Schaden durch ein Schwert. Als Reaktion darauf tritt A's Rüstung ein und verringert den erlittenen Schaden. Die Bewegungsgeschwindigkeit von A wird ebenfalls verringert, weil es schwächer wird.
- Der erlittene Schaden löst die gesamte Interaktion aus
- Die Rüstung muss berechnet und auf den ankommenden Schaden angewendet werden, bevor der Schaden auf den Spieler angewendet wird
- Die Reduzierung der Bewegungsgeschwindigkeit kann erst dann auf eine Einheit angewendet werden, wenn der Schaden tatsächlich verursacht wurde, da dies von der endgültigen Schadensmenge abhängt.
Ereignisse können auch andere Ereignisse auslösen. Das Reduzieren des Schwertschadens mithilfe von Rüstungen kann dazu führen, dass das Schwert zerbricht (dies muss erfolgen, bevor die Schadensreduzierung abgeschlossen ist), was wiederum zusätzliche Ereignisse als Reaktion darauf verursachen kann, im Wesentlichen eine rekursive Bewertung von Ereignissen.
Alles in allem scheint dies zu einigen Problemen zu führen:
- Viele verschwendete Verarbeitungszyklen: Die meisten Systeme (abgesehen von Dingen, die immer ausgeführt werden, wie z. B. das Rendern) haben einfach nichts Wertvolles zu tun, wenn sie nicht an der Reihe sind, zu arbeiten, und verbringen die meiste Zeit damit, auf den Eintritt des Spiels zu warten ein gültiger Arbeitszustand. Dies verschmutzt jedes dieser Systeme mit Schecks, die immer größer werden, je mehr Zustände dem Spiel hinzugefügt werden.
- Um herauszufinden, ob ein System Entitäten verarbeiten kann, die im Spiel vorhanden sind, müssen sie andere nicht verwandte Entitäts- / Systemzustände überwachen (das System, das für das Verursachen von Schaden verantwortlich ist, muss wissen, ob eine Rüstung angewendet wurde oder nicht). Dies verwirrt entweder die Systeme mit mehreren Verantwortlichkeiten oder macht zusätzliche Systeme ohne anderen Zweck erforderlich, als die Entitätssammlung nach jedem Verarbeitungszyklus zu scannen und mit einer Reihe von Listenern zu kommunizieren, indem ihnen mitgeteilt wird, wann es in Ordnung ist, etwas zu tun.
Bei den beiden oben genannten Punkten wird davon ausgegangen, dass die Systeme mit derselben Gruppe von Entitäten arbeiten, die ihren Status mithilfe von Flags in ihren Komponenten ändern.
Eine andere Möglichkeit, dies zu lösen, wäre das Hinzufügen / Entfernen von Komponenten (oder das Erstellen völlig neuer Entitäten) als Ergebnis einer einzelnen Systemarbeit, um den Spielstatus zu verbessern. Dies bedeutet, dass ein System, wenn es tatsächlich eine übereinstimmende Entität hat, weiß, dass es diese verarbeiten darf.
Dies macht Systeme jedoch für das Auslösen nachfolgender Systeme verantwortlich, was es schwierig macht, über das Programmverhalten nachzudenken, da Fehler nicht als Ergebnis einer einzelnen Systeminteraktion auftreten. Das Hinzufügen neuer Systeme wird auch schwieriger, da sie nicht implementiert werden können, ohne genau zu wissen, wie sie sich auf andere Systeme auswirken (und frühere Systeme möglicherweise geändert werden müssen, um die Zustände auszulösen, an denen das neue System interessiert ist), was den Zweck separater Systeme irgendwie zunichte macht mit einer einzigen Aufgabe.
Muss ich damit leben? Jedes einzelne ECS-Beispiel, das ich gesehen habe, war in Echtzeit, und es ist wirklich leicht zu sehen, wie diese One-Iteration-per-Game-Schleife in solchen Fällen funktioniert. Und ich brauche es immer noch zum Rendern, es scheint nur wirklich ungeeignet für Systeme zu sein, die jedes Mal, wenn etwas passiert, die meisten Aspekte von sich selbst anhalten.
Gibt es ein Entwurfsmuster zum Vorwärtsbewegen des Spielstatus, das dafür geeignet ist, oder sollte ich einfach die gesamte Logik aus der Schleife verschieben und sie stattdessen nur bei Bedarf auslösen?