Spielstatus und Eingabehandhabung in komponentenbasierten Entitätssystemen

16

Meine Frage ist:

Wie kann ich mit Spielzuständen in meinem Entitätssystem umgehen, ohne einen Stapel von Spielzustandsobjekten zu behalten?

Wenn sich eine Entität beispielsweise für Eingabeereignisse registrieren muss, ruft die Eingabekomponente das Eingabesystem auf und sagt "Diese Entität für diese Eingabe registrieren". Dies ist alles in Ordnung, aber wenn Sie das Konzept der Spielzustände hinzufügen (sagen wir einen Pausenbildschirm), wird es zu einem Problem, herauszufinden, ob sich eine Entität im aktuellen Zustand befindet und die Eingabe erhalten sollte.

Ich könnte die Eingabekomponente / das Eingabesystem so erweitern, dass dort steht: "Diese Entität für diese Eingabe registrieren, während sie sich in diesen Spielzuständen befindet". Dies setzt jedoch voraus, dass jede Entität weiß, in welchen Zuständen sie verwendet wird, und das ist möglicherweise nicht offensichtlich. Außerdem klingt es nicht effizient, eine Liste der Spielzustände für jeden registrierten Eingang (und für andere Systeme, die Rückrufe verwenden) zu führen.

Eine andere Idee, die ich hatte, ist, dass es eine Entität geben wird, die den Spielstatus darstellt, diese als deaktiviert markiert und dann beim Generieren des Eingabeereignisses überprüft, ob die Entität kein Abkömmling einer deaktivierten Spielstatus-Entität ist. Es scheint teuer zu sein, den Elternteil für jeden Rückruf zu ermitteln.

Eine andere Idee ist, dass alle Systeme ihre Daten mit dem aktuellen Status abspeichern. Auf diese Weise wird die Zielentität beim Generieren der Eingabe nicht einmal ein Kandidat sein. Dies schadet jedoch wirklich der Möglichkeit, die Kommunikation zwischen Entitäten in verschiedenen Zuständen zuzulassen (nicht so sehr ein Problem für Pausenbildschirme, sondern das Aufheben von Sperren in Oblivion / Skyrim).

Die einzige andere Idee, die ich hatte, ist, dass alle Komponenten Zustandsänderungsereignisse verarbeiten und mit ihrem jeweiligen System kommunizieren, um alles zu deaktivieren, was sie registriert haben, und es wieder zu aktivieren, wenn sie in diesen Zustand zurückkehren.

Das zweite (ein Objekt als deaktiviert markieren) und vierte (jede Komponente muss sich mit Zustandsänderungen befassen) scheinen meine besten Ideen zu sein, aber keine von ihnen spricht mich als besonders großartig an.

Hat jemand andere Ideen, wie man das macht?

Bearbeiten Während ich in dieser Frage speziell über Eingaben spreche, kann dies jedes System bedeuten, das Nachrichten / Ereignisse an Entitäten senden kann, wie z. B. Kollisionen, Timer-Ereignisse usw.

elFarto
quelle
6
Ich mache es so: Ich habe Bildschirme, MenuScreen PauseScreen GameScreen, jeder Bildschirm kann seine eigene Welt (Container für Entities) und Systeme (wie RenderingSystem) erstellen und dann in GameScreen erstelle ich World, Entity with CameraComponent und setze CameraComponent.RenderTarget auf Bildschirmhintergrund. Auf diese Weise kann ich InventoryScreen hinzufügen, das eigene Entitäten und Systeme haben wird (wie vereinfachter Renderer). Eingaben können vom Bildschirm an die Welt weitergegeben werden, sodass Ihre Benutzeroberfläche entscheidet, ob Eingaben an den Bildschirm weitergegeben werden (sofern sie fokussiert, sichtbar usw. sind) und ob Eingaben an die Welt und an Entitäten weitergegeben werden
Kikaimaru,
2
@Byte56 Nicht wirklich, nur der erste hat mit Gamestates zu tun (die anderen 2 sind Zustände innerhalb von Entities), und das packt nicht wirklich das gleiche Problem an, das ich habe. Wenn sich das Spiel im pausierten Zustand befindet, muss etwas mit dem Eingabesystem geschehen, um zu verhindern, dass es Bewegungsmeldungen an die Spieler-Entität sendet (zum Beispiel). Ich kann einfach keinen guten Weg finden, dies zu tun.
ElFarto
1
OK, betrachten Sie sie dann als verwandt. Gute Frage.
MichaelHouse
1
Noch etwas zu berücksichtigen, das meine komponentenbasierten Systeme in der Vergangenheit gestört hat: die mehrschichtige Benutzeroberfläche. Popup-Dialog auf Welt- oder Mehrebenenbildschirmen. Es ist in jedem Spiel, das ich gemacht habe, so weit gekommen, dass ich sagen würde, dass man einen Ansatz in Betracht ziehen sollte, der dieses Problem lösen kann.
ADB

Antworten:

14

Was häufig verwendet wird, ist ein Zwischenprodukt, Intent Systemdas die Eingabe abstrahiert und den Kontext und die relevanten Spielzustände verfolgt.

Das Intent-System beendet die Übertragung von Eingaben, wenn die Simulation beispielsweise angehalten wird. Es behandelt auch die Zuordnung zwischen Controller-Ereignissen und Absichten (in Richtung bewegen, rennen, schießen, neu laden ...).

Auf diese Weise sind Ihre anderen Komponenten nicht von bestimmten Gamepads / Eingaben abhängig (BUTTON_A, BUTTON_B vs BUTTON_X, BUTTON_O ...), sondern reagieren alle auf die gleichen Absichten (IntentRun, IntentReload ...).

Ein weiterer Vorteil ist, dass das Intent-System erkennt, dass verfügbare Controller hinzugefügt / entfernt werden, da es Intents an jeden Teilnehmer senden kann, auch außerhalb der Simulation, die Sie für Intents verwenden können AddPlayer(controllerID).

Wie viele Informationen über den Spielstatus Sie dem System entweder über Ereignisse / Nachrichten oder direkt zur Verfügung stellen, bleibt Ihnen überlassen. Aber die Zeit, die in das Intent-System investiert wird, lohnt sich normalerweise.

Sie können Absichtskontexte verwalten, die Absichten generieren, wenn sie mit dem System verbunden werden.

Der Kontext kann priorisiert werden, dh:

  • SimulationAvailableContext sendet Absichten an die Simulation, solange diese verfügbar ist (aber nicht ausgeführt wird), z. B. Kamera bewegen, vergrößern, verkleinern, Player hinzufügen / entfernen ...
  • SimulationRunningContext sendet Absichten an die Simulation, während sie nicht angehalten ist. Spieler bewegen, Einheit an Position senden, schießen ...

Auf diese Weise können Sie die aktuell relevanten Kontexte hinzufügen und entfernen.

Und eine Sache bei den gesamten Vorsatzsystemen ist, dass sie ausgeführt werden sollten, während die Simulation angehalten ist.

Eine Möglichkeit, die häufig verwendet wird, um die Spielsimulation zu spielen / anzuhalten, ohne nicht mit der Simulation in Zusammenhang stehende Aktualisierungen zu unterbrechen, besteht darin, unterschiedliche Zeitspannen zu verwenden. dh GenericSystem::onTime(Long time, Long deltaTime, Long simTime, Long simDeltaTime).

Mit diesem Ansatz kann Ihre Engine einfach die Inkremente in der SimTime des Spiels blockieren, was wiederum Aktualisierungen der relevanten Animations- und Physik-Engines blockiert und simTime and simDeltaTimegleichzeitig kontinuierliche Aktualisierungen Ihres Kamera -Federeffekts ermöglicht, wenn er sich auch während der Pause, der Animation von, bewegen muss der Ladeeffekt auf einer virtuellen In-Game-Werbetafel, während Daten heruntergeladen werden ...

Kojote
quelle
Ich mag die Tatsache, dass dies nicht eine Reihe von "State Changed" -Funktionen für alle Entitäten aufrufen muss. Sie müssen sich Sorgen machen, dass die falschen Absichten zur falschen Zeit gesendet werden, aber ich denke, das ist besser als die Alternative.
Thomas Marnell
Ihre Wesen können Absichten wie Springen ignorieren, während ihr Zustand es ihnen nicht erlaubt zu springen (dh den Boden nicht zu berühren). Aber sie müssen sich keine Sorgen machen, solche Absichten zu erhalten, während das Spiel unterbrochen ist.
Coyote
Ich hatte bereits darüber nachgedacht, die Entität dem Eingabesystem mitteilen zu lassen, in welchen Zuständen Nachrichten zugestellt werden sollen, aber ich hatte nicht daran gedacht, die Zustände der Eingabe selbst zuzuweisen, was eine gute Idee ist. Auch das Aufteilen von Zeit und SimTime ist schön.
ElFarto
Sie sollten vermeiden, Ihren simulationsbezogenen Status mit nicht simulationsbezogenen Dingen aufzublähen. Bewegen Sie die gesamte Benutzeroberfläche und den spielerbezogenen Code so weit wie möglich von der Simulation entfernt und konzentrieren Sie sich in der Simulation nur auf die Absichten.
Coyote
Hey @Coyote, dieses System klingt sehr interessant. Könnten Sie vielleicht weitere Informationen zur Verfügung stellen, indem Sie diese Frage beantworten ? Vielen Dank!
pek
2

Wie wäre es mit der Erstellung eines globalen Ereignissystems und einer Ereignis-Listener-Komponente für jede Entität? Nach einem Event "Game State Change" konnte man für jede Entität individuell mit Komponenten experimentieren.

Angenommen, Sie haben eine Eingabekomponente. Nachdem die Ereignis-Listener-Komponente das Spielstatusänderungsereignis empfangen hat, ändert sie sehr spezifische Werte für diese bestimmte Eingabekomponente, sodass sie keine Eingabeaufrufe empfängt oder keine Bewegungs- oder Antwortaufrufe an das System oder dessen Besitzer ausführt.

Dies funktioniert bei mir, da die meisten meiner Komponenten per Skript (über Lua) erstellt wurden. Das heißt, ich habe eine Eingabekomponente, die einmal ausgelöst wird, wenn eine Taste gedrückt wird und eine Bewegung + Richtung ausgelöst wird, und dann ausgelöst wird, wenn die Taste losgelassen wird und eine Stop + Richtung ausgelöst wird. Es gibt auch eine Ereignis-Listener-Komponente, die die Eingabekomponente kontaktiert (wenn das Spiel unterbrochen ist), um das Auslösen von Funktionen zu stoppen und erforderlichenfalls anzuhalten. Ich könnte dann leicht eine andere Entität mit einer anderen Reaktion auf dieselben Ereignisse und Tastendrücke unter Verwendung eines anderen Skripts hinzufügen. Auf diese Weise können Sie die Interaktion zwischen verschiedenen Entitäten in verschiedenen Zuständen speichern und sogar noch anpassbarer gestalten. Darüber hinaus enthalten einige Entitäten möglicherweise nicht einmal die Ereignis-Listener-Komponente.

Was ich gerade erklärt habe, ist im Grunde genommen ein praktisches Beispiel für Ihre vierte Lösung.

karmalis
quelle