Wie kann eine Finite-State-Maschine in eine komponentenbasierte Architektur eingebunden werden? [geschlossen]

23

Zustandsautomaten scheinen schädliche Abhängigkeiten in komponentenbasierten Architekturen zu verursachen.

Wie wird konkret die Kommunikation zwischen einer Zustandsmaschine und den Komponenten gehandhabt, die zustandsbezogenes Verhalten ausführen?

Wo ich bin:

  • Ich bin neu in komponentenbasierten Architekturen.
  • Ich mache ein Kampfspiel, obwohl ich nicht denke, dass das wichtig sein sollte. Ich stelle mir vor, dass meine Zustandsmaschine verwendet wird, um Zustände wie "ducken", "stürzen", "blockieren" usw. umzuschalten.
  • Ich habe festgestellt, dass diese State-Management-Technik das natürlichste System für eine komponentenbasierte Architektur ist, aber sie widerspricht den Techniken, über die ich gelesen habe: Dynamisches Spielobjekt-Komponentensystem für Charaktere mit veränderlichem Verhalten Es wird vorgeschlagen, dass alle Komponenten aktiviert / deaktiviert werden sich selbst, indem sie ständig eine Bedingung für die Aktivierung überprüfen.
  • Ich denke, dass Aktionen wie "Laufen" oder "Gehen" als Status sinnvoll sind, was mit der hier akzeptierten Antwort nicht übereinstimmt: /gamedev//a/7934
  • Ich fand das nützlich, aber nicht eindeutig: Wie implementiere ich Verhalten in einer komponentenbasierten Spielarchitektur? Es wird vorgeschlagen, eine separate Komponente zu haben, die nur eine Zustandsmaschine enthält. Dies erfordert jedoch eine Art Kopplung zwischen der Zustandsmaschinenkomponente und nahezu allen anderen Komponenten. Ich verstehe nicht, wie diese Kopplung behandelt werden soll. Dies sind einige Vermutungen:

    A. Komponenten sind von der Zustandsmaschine abhängig:
    Komponenten erhalten Verweise auf Zustandsmaschinenkomponenten getState(), die eine Aufzählungskonstante zurückgeben. Komponenten aktualisieren sich regelmäßig und prüfen dies nach Bedarf.

    B. Die Zustandsmaschine hängt von den Komponenten ab:
    Die Zustandsmaschinenkomponente erhält Verweise auf alle Komponenten, die sie überwacht. Es fragt ihre getState()Methoden ab, um zu sehen, wo sie sich befinden.

    C. Eine Abstraktion zwischen ihnen
    Verwenden Sie einen Event Hub? Befehlsmuster?

    D. Es werden separate Statusobjekte verwendet, die auf das Statusmuster der Komponenten verweisen
    . Es werden separate Statusobjekte erstellt, die eine Reihe von Komponenten aktivieren / deaktivieren. Zustandsmaschine wechselt zwischen Zustandsobjekten.

  • Ich betrachte Komponenten als Implementierungen von Aspekten . Sie tun alles, was intern benötigt wird, um diesen Aspekt zu verwirklichen. Es scheint, als ob Komponenten von selbst funktionieren sollten, ohne auf andere Komponenten angewiesen zu sein. Ich weiß, dass einige Abhängigkeiten erforderlich sind, aber Statusmaschinen scheinen alle meine Komponenten steuern zu wollen.

Welpe
quelle

Antworten:

7

Die Übersicht ist ziemlich leicht, aber sieh dir diese Folien aus einer Präsentation an, die ich letztes Jahr für New Game Conf gemacht habe:

https://docs.google.com/presentation/d/110MxOqut_y7KOW1pNwIdcccisIA3ooJwVR-xm-ecuc4/view

(siehe entsprechende Bilder unten)

Der Kern der Technik besteht darin, das Aktionslistenmuster (das in den Folien etwas schlecht erklärt wurde) mit Verhaltenszustandsautomaten zu kombinieren, die auf eine komponentenbasierte Spieleinheit einwirken.

Es ist im Wesentlichen dasselbe wie das Erstellen eines speziellen Kompositionssystems nur für das KI-Verhalten, das auf die Arten der Integration zwischen Verhalten ausgerichtet ist, die Sie für einfachere KI-Systeme benötigen.

Mein Lieblingsteil dieses bestimmten Spiels war, wie wir völlig neue Arten von Feinden erschaffen konnten, indem wir einfach aus einer Liste vorab geschriebener Verhaltensweisen auswählten und sie in der gewünschten Reihenfolge in die Aktionsliste für das Spielobjekt (das sich in einer BrainComponent befindet) einsetzten Priorität, und alles hat einfach funktioniert. Mit einer Aktionsliste, die das Blockieren / Nicht-Blockieren von Aktionen ermöglicht, kann dies einige wirklich coole Dinge bewirken, trotz der einfachen Implementierung.

Sogar Verhaltensweisen wie "betäuben", bei denen wirklich nur eine StunBehaviorAction ganz oben auf dem Aktionslistenstapel platziert wurde; Wenn das Betäubungsverhalten aktiviert wird (nachdem festgestellt wurde, dass die EarsComponent des Spielobjekts einen atemberaubenden Stoßwellenangriff gehört hat), setzt sie ihren internen Status auf Betäubt, weist die AnimationComponent an, die Betäubungsanimation abzuspielen, und setzt ihren Aktionsstatus auf Blockieren und ihren Timer auf Ein Betäubungs-Timeout, das von der EnemyParametersComponent des Spielobjekts abgerufen wurde. Da es sich um Blocking handelte und ganz oben in der Aktionsliste stand, wurde für keine der anderen BehaviorActions in der Aktionsliste die Aktualisierungsmethode aufgerufen, sodass sie im Wesentlichen ausgeschaltet waren. Wenn das Zeitlimit abgelaufen ist, hat die StunBehaviorAction ihren Status wieder auf Leerlauf und ihren Aktionsstatus auf Nicht blockierend gesetzt.

Die anderen von uns implementierten Verhaltensweisen wurden fast alle mit einer einzigen internen Zustandsmaschine implementiert. Die einzigen zwei, die keine Zustandsautomaten hatten, waren die PatrolPathBehaviorAction (sie würde eine Reihe von PathActions auf die Aktionsliste setzen, wenn sie im Leerlauf war, was wiederum MoveActions drückte) und die GuardHomeBehaviorAction (immer am unteren Rand der Aktionsliste, und würde immer nur eine PathAction zum Heimatort des Feindes zurückschieben). Jedes andere Verhalten war eine Zustandsmaschine.

Folie 10 Folie 25 Folie 26

Sean Middleditch
quelle
Was ist der grundlegende Unterschied zwischen "Verhalten" und "Aktionen"?
Welpe
1
@Pup: Aus der Sicht des Codes ist ein Verhalten, wie ich es erstellt habe, eine Aktion. Aus konzeptioneller Sicht sind Aktionen in der Regel vorübergehend - sie existieren nur bis "vollständig" -, während Verhalten für immer und niemals von der Liste gestrichen werden. Ich habe gesehen, wie ein anderes Team ein ähnliches System aufgebaut hat, aber mit zwei Listen, eine für Aktionen und eine für Verhalten, was gut genug funktioniert. Ich mag es, wenn eine Aktion bestimmte Verhaltensweisen blockiert, indem ich Bitmasken und Gruppierungen verwende (ich glaube, ich habe sie in den Folien als Spuren bezeichnet). Tut mir leid, dass die Grafik der mittleren Folie so schlecht ist. :)
Sean Middleditch
3

In einer früheren Firma, für die ich gearbeitet habe, hatten wir ein komponentenbasiertes System mit staatlicher KI. Wir hatten eine KI-Komponente, die das gesamte Verhalten für dieses Objekt / diese Einheit kontrollierte. Wenn die KI aktiv war, wie z. B. umherwandern, angreifen usw., erhielt sie für jeden Frame ein Update, um die erforderliche Logik auszuführen. Wenn die KI im Leerlauf war oder sich nicht bewegte, wurde die Komponente deaktiviert und nicht für jeden Frame aktualisiert. Die Komponente kann, wenn sie deaktiviert ist, immer noch ereignisbasierte Nachrichten empfangen, so dass sie eine Nachricht für einen Spieler erhält, der ihren Aggro-Radius eingibt, und kann darauf reagieren, indem sie die AI-Komponente reaktiviert, so dass sie rahmenbasierte Aktualisierungen durchführen kann.

Die KI-Komponente verfügt über Unterkomponenten, die je nach Art der durchgeführten Aktionen im Handumdrehen erstellt und zerstört werden können. Wenn es zum Beispiel wanderte, könnte es eine wandernde Unterkomponente erstellen und jeden Frame während des Wanderns aktualisieren. Wenn es während des Wanderns aggro würde, würde es diese Unterkomponente schließen und eine Angriffs-Unterkomponente öffnen. Die AI-Komponente sollte unabhängig von allen anderen Komponenten eines Objekts sein. Wir hatten zum Beispiel eine Eingabekomponente, die einfach nach Bewegungswerten für eine Einheit abfragt. Die Abfrage war etwas, auf das sowohl menschliche als auch KI-Objekte antworten würden. Dies ermöglichte es der KI-Komponente, während des Wanderns einfach Bewegungswerte für sich selbst festzulegen, auf die die Eingabekomponente zugreifen konnte, genau wie eine vom Menschen steuerbare Komponente Werte festlegen würde, auf die die Eingabekomponente zugreifen konnte.

Nic Foster
quelle
Also machen die AI-Unterkomponenten tatsächlich die Arbeit? Gab es sie als Entitätskomponenten auf der gleichen Ebene wie die AI-Komponente?
Pup
Unterkomponenten in unserem Motor waren Teil der Basiskomponentenklasse. Also könnte jeder Component, der davon abgeleitet ist BaseComponent, eine beliebige Anzahl von SubComponents haben. Die Update()Methode in BaseComponentwürde die Liste der Unterkomponenten prüfen und diese aufrufen Update(). Subcomponentswaren völlig optional, so dass die BaseComponentmöglicherweise keine haben. Alle Nachrichten, die an eine Komponente gesendet wurden, wurden auch an die Unterkomponenten weitergeleitet.
Nic Foster
1

Es ist ein wenig unklar, was Sie unter Komponenten verstehen, da Ihre Begriffe sehr vage sind und keine konkreten Beispiele enthalten. Oft werden Spieleinheiten eher mit Komposition als mit Vererbung erstellt. Auf diese Weise können Sie sie zu etwas machen, das Schaden nehmen kann, indem Sie der Entität eine Integritätskomponente hinzufügen, oder Sie können sie durch Hinzufügen einer Animationskomponente animieren. Man könnte die KI auch in eine solche Komponente einbauen. In Ihrer KI-Komponente gibt es Entscheidungslogik. Wenn Sie Bedenken haben, diese mit einem Großteil des anderen Codes im System zu koppeln, können Sie die Informationen in einer Tafel zusammenfassen, auf die die KI-Logik nur zugreifen darf. Es gibt auch das Problem der Abhängigkeiten von der Ausgabe des AI-Systems. Grundsätzlich kontrolliert Ihre KI eine Entität und diese Kontrolle benötigt eine Schnittstelle. Ein nützliches Konzept ist das eines Controllers oder eines Gamepads. Ihre KI kann eine ähnliche Struktur ausfüllen, die ein Spieler-Gamepad ausfüllen würde (obwohl es einige zusätzliche "Schaltflächen" für bestimmte Fähigkeiten haben könnte). Diese Struktur kann nun an Ihre Animationskomponente übergeben werden, die sie interpretiert und die entsprechenden Animationen für die Wiedergabe auswählt. Verschiedene KI-Unterkomponenten können sogar in verschiedene Felder der Struktur oder in dieselben Felder mit unterschiedlichen Prioritäten schreiben. Zum Beispiel zielen und laufen. Verschiedene KI-Unterkomponenten können sogar in verschiedene Felder der Struktur oder in dieselben Felder mit unterschiedlichen Prioritäten schreiben. Zum Beispiel zielen und laufen. Verschiedene KI-Unterkomponenten können sogar in verschiedene Felder der Struktur oder in dieselben Felder mit unterschiedlichen Prioritäten schreiben. Zum Beispiel zielen und laufen.

Jesse Cluff
quelle