Ich fange an, Spieler- und Feind-KI in einem Spiel zu implementieren, bin jedoch verwirrt, wie dies am besten in einer komponentenbasierten Spielarchitektur implementiert werden kann.
Angenommen, ich habe einen folgenden Spielercharakter, der stationär sein, ein Schwert führen und schwingen kann. Ein Spieler kann sowohl aus dem stationären als auch aus dem laufenden Zustand in den Schwungschwertzustand übergehen, aber dann muss der Schwung abgeschlossen sein, bevor der Spieler wieder stehen oder herumlaufen kann. Während des Schwungs kann der Spieler nicht herumlaufen.
Aus meiner Sicht habe ich zwei Implementierungsansätze:
- Erstellen Sie eine einzelne KI-Komponente mit der gesamten Player-Logik (entweder von der eigentlichen Komponente abgekoppelt oder als PlayerAIComponent eingebettet). Ich kann auf einfache Weise die staatlichen Beschränkungen durchsetzen, ohne eine Kopplung zwischen den einzelnen Komponenten herzustellen, aus denen sich die Player-Entität zusammensetzt. Die AI-Komponente kann jedoch nicht aufgelöst werden. Wenn ich zum Beispiel einen Feind habe, der nur stehen und herumlaufen kann oder nur herumläuft und gelegentlich ein Schwert schwingt, muss ich neue KI-Komponenten erschaffen.
- Teilen Sie das Verhalten in Komponenten auf, von denen jede einen bestimmten Status angibt. Ich bekomme dann eine StandComponent, WalkComponent und SwingComponent. Um die Übergangsregeln durchzusetzen, muss ich jede Komponente koppeln. SwingComponent muss StandComponent und WalkComponent für die Dauer des Swings deaktivieren. Wenn ich einen Gegner habe, der nur herumsteht und gelegentlich ein Schwert schwingt, muss ich sicherstellen, dass SwingComponent WalkComponent nur deaktiviert, wenn es vorhanden ist. Dies ermöglicht zwar ein besseres Mischen und Anpassen von Komponenten, kann jedoch zu einem Albtraum der Wartbarkeit führen, da die vorhandenen Komponenten jedes Mal aktualisiert werden müssen, wenn eine Abhängigkeit hinzugefügt wird, um den neuen Anforderungen gerecht zu werden, die die Abhängigkeit an den Charakter stellt.
Die ideale Situation wäre, dass ein Designer neue Feinde / Spieler aufbauen kann, indem er Komponenten in einen Container zieht, ohne eine einzelne Zeile des Engine- oder Skriptcodes berühren zu müssen. Obwohl ich nicht sicher bin, ob eine Skriptcodierung vermieden werden kann, möchte ich sie so einfach wie möglich halten.
Alles in allem: Soll ich die gesamte KI-Logik in eine Komponente unterteilen oder jeden Logikzustand in separate Komponenten aufteilen, um Entitätsvarianten einfacher zu erstellen?
edit : Ich vermute, es gibt einige Verwirrung darüber, was ich mit der ersten und zweiten Situation gemeint habe. Ich habe versucht, es in der folgenden Abbildung zu erklären.
Beachten Sie die Beziehung zwischen den einzelnen Staaten und der Entität. In der ersten Situation wird eine AI-Komponente vorab erstellt, bevor sie in die Entität eingefügt wird. Ein Designer kann nur aus einem bestimmten Satz von AIComponents auswählen, die vom Programmierer zur Verfügung gestellt werden. Die zweite Situation hat die verschiedenen Zustände auf der gleichen Ebene wie andere Komponenten. Ein Designer kann jetzt eine Entität mit eindeutiger KI erstellen, ohne dass ein Programmierer eingreift.
Die Frage ist, ob dies die einzigen beiden Optionen für die Strukturierung von KI in einer komponentenbasierten Entität sind. Wenn ja, welche bieten die maximale Flexibilität?
quelle
Antworten:
Wenn Sie mehr mögliche Gegner oder Spieler haben möchten, die Sie sich derzeit nicht vorstellen können, sollten Sie dies definitiv auflösen. Was Sie in Ihrem zweiten Punkt beschreiben, ist im Grunde das Zustandsmuster .
Ich glaube, ich stimme Gregory zu, dass Sie keine separaten Stand- und Gehzustandskomponenten haben sollten. Es ist nur eine Bewegungskomponente mit Geschwindigkeit 0. Wenn Sie dagegen Objekte haben, die sich nicht bewegen können, müssen Sie diese entweder aufteilen oder einfach eine Art boolesche Einschränkung in den Bewegungszustand einfügen, die verhindert, dass die Geschwindigkeit nicht Null ist .
Für den Spieler glaube ich nicht, dass es völlig getrennt sein muss. Es können weiterhin alle anderen Komponenten verwendet werden, wobei eine Eingabekomponente hinzugefügt wird. Diese Komponente steuert die Übergänge zwischen Zuständen, während sie im Feind von einer Standard-KI gesteuert wird, oder wenn Sie möchten, von verschiedenen KI-Unterklassen, aus denen Ihre feindlichen Konstrukteure auswählen können.
Bearbeiten: Geben Sie Ihren stationären Gegnern, anstatt die Bewegungskomponente einzuschränken, einfach eine stationäre KI-Komponente, die sie niemals bewegt.
quelle
Ich würde zumindest die Player-KI (oder was ich als Player-Controller bezeichnen würde) als eigene Komponente beibehalten. Bei den meisten Spielen unterscheidet sich der Spieler grundlegend genug von den NPCs, dass Sie nur in Grundlagen wie Trefferpunkten von einem zum anderen generalisieren können.
Für NPCs sehe ich StandComponent und WalkComponent als Aspekte derselben Sache. Wirst du jemals eine WalkComponent ohne StandComponent haben? Ich bezweifle das. Ebenso wäre eine RunComponent nur eine WalkComponent mit einer höheren Geschwindigkeit und unterschiedlichen Animationen. Ich kann den Wert erkennen, wenn ich eine NPCMovementComponent und eine separate NPCSwordFighterComponent habe, aber selbst das scheint mir ein Überentwicklungsprozess zu sein.
quelle
Zuerst würde ich eine Zustandskomponente erstellen und dann eine Zustandsmaschine, um die Übergänge zu handhaben. Machen Sie es so allgemein, dass Sie es für Ihre Spieler und Ihre KI verwenden können. Dies stellt sicher, dass die KI nach den gleichen Regeln spielt und dass Sie Ihre Logik nicht ändern müssen, wenn Sie die Funktionsweise der Player-Zustände im Vergleich zu den KI-Zuständen ändern.
Finite State Machine C ++
Das obige Beispiel zeigt ein konkretes Beispiel für eine Zustandsmaschine in c ++, die von Spielern und KI gleichermaßen verwendet werden kann.
quelle
Was Sie wollen, ist eine Komponente, die die Bewegung von Charakteren (Spieler und NPCs) handhabt. Die KI-Komponente oder eine Spielerkomponente sendet Befehle an diese Bewegungskomponente und prüft, ob die Aktion ausgelöst werden kann. Dadurch werden Ihre Bewegungseinschränkungen in einer einzigen Komponente zusammengefasst. Ihr KI-Code und der Spielercode müssen nicht wissen, wie das Swing-Schwert ausgeführt wird. Die KI würde interne Zustände haben, z. B. Leerlauf, Angriff, Flucht.
quelle