ECS: AI-Komponenten und -Systeme


9

Ich versuche mit einem ECS das beste Entwurfsmuster für meinen AI-Code zu finden.

Im Moment haben die Entitäten, die als CPU-gesteuert fungieren, Komponenten wie:

  • WeaponComponent
  • ChargeComponent
  • Bewegungskomponente
  • AIControlledComponent

...

Ich habe ein AIControlSystem , das die Entitäten mit der AIControlledComponent überprüft und während jedes Frames den mentalen Zustand der Entität basierend auf einigen Regeln wie "Kann ich den Player sehen?", "Kann ich mich an einen sicheren Ort bewegen?" Usw. wechselt. .

Basierend auf dem mentalen Zustand fügt dieses System einer Warteschlange eine Reihe von Aktionen hinzu.

Dann wird die Warteschlange verbraucht und ausgewertet. Eine Aktion wie "Attack" ruft tatsächlich eine Methode des Systems auf, die prüft, ob an die Entität eine WeaponComponent angehängt ist und ausgelöst wird oder ob eine ChargeComponent vom Spieler aufgeladen wird. Auf diese Weise wachsen diese Methoden tendenziell, wenn die Art der Feinde und Aktionen zunimmt und der Code zu einem Chaos wird, da es viel zu viele Überprüfungen und Pfade gibt, denen man folgen kann.

Ich dachte, ich könnte den Code, der ihn aufteilt, basierend auf der Art des Feindes (auch bekannt als ein Feind, der eine Waffe hat, kann nicht angreifen und umgekehrt) umgestalten, aber dann müsste ich ein Basis-AIControlSystem haben und davon erben und spezialisieren Sie es basierend auf den Komponenten, die an die Entitäten angehängt sind. Dies würde zu so ziemlich einem neuen System pro feindlicher Art führen und es sieht nicht wirklich nach einer guten Lösung aus.

Ich dachte darüber nach, von dem reinen ECS-Muster abzuweichen, Komponenten nur als Datencontainer zu behalten und diese Methoden tatsächlich in den Komponenten selbst zu implementieren, damit ich eine Basis-AIControlledComponent mit den virtuellen Methoden haben kann, die die Warteschlange von Aktionen interpretieren und eine neue Komponente davon erben eine für jeden Feindtyp. Für die Code-Organisation sieht es ein bisschen besser aus, aber ich bin mir nicht sicher, ob es eine gute Idee ist, diese Art von Ausnahme zu machen.

Gibt es eine andere Möglichkeit, den Code unter Kontrolle zu halten und gleichzeitig die erforderliche Flexibilität zu haben?


6
KI ist genau eines der Dinge, die mich dazu bringen, den strengen ECS-Unsinn, den Reddit-Hobbyisten heutzutage betreiben, nicht zu mögen. Die Verwendung von Komponenten ist gut. Es ist gut, für einige Dinge einen ECS-ähnlichen Ansatz zu verwenden . Die Verwendung eines strengen ECS für alles erfordert unnötige Schwierigkeiten.
Sean Middleditch

@ SeanMiddleditch Ich stimme deinem Gefühl zu. Ich habe evaluiert, meine AIControlledComponent in eine Art Tafel zu verwandeln und alles andere, was mit KI zu tun hat, in ein ganz anderes System zu verschieben, das das ECS nicht direkt verwendet. Wenn Sie Hinweise zum Entwerfen von so etwas haben, wäre dies sehr willkommen.
Valerio Santinelli

Keines Ihrer Hauptmodule sollte überhaupt die ECS-Header enthalten dürfen. Verwenden Sie Komponenten nur, um Module zu Spielobjekten zusammenzukleben. Ihr gesamter Kern-Engine-Code sollte mit jedem Objektmodell verwendbar sein. Grafiken rendern Szenenknoten, Physik bewegt Körper, Kern-KI interpretiert Entscheidungsgraphen usw. Keine davon basiert auf tatsächlichen Spielobjekten. Bestimmte KI-Knoten sind natürlich vom Spielstatus abhängig, sodass das übergeordnete KI-System möglicherweise Komponenten verwendet, um Daten oder Bäume an das untergeordnete KI-Modul weiterzuleiten, aber das Modul hängt überhaupt nicht von den Komponenten ab.
Sean Middleditch

@ SeanMiddleditch Das Problem, das ich bei diesem Ansatz sehe, ist, dass das AI-Modul nicht einfach von den an die Entitäten angeschlossenen Komponenten entkoppelt werden kann. Um einen Baum auszuwerten oder eine Zustandsmaschine auszuführen, muss sie Informationen von der verwendeten Entität kennen, damit sie tatsächlich Informationen wie das Inventar, die verfügbaren Waffen, die Position usw. aus den tatsächlichen Komponenten abrufen kann. Wie würden Sie das entkoppeln?
Valerio Santinelli

Ich war vielleicht nicht klar. Das AI-Modul selbst muss es nicht wissen, da es die konkrete Entscheidungslogik in den Bewertungsknoten nicht kennen muss. Dies sind "Plugins" (Schnittstellenimplementierungen), die der übergeordnete Gameplay-Code bereitstellen kann.
Sean Middleditch

Antworten:


3

The problem I see with this approach is that the AI module cannot be easily decoupled from the components attached to the entities. To evaluate a tree or run a state machine, it needs to know information from the entity being used so it actually needs to pull information like the inventory, the weapons available, the position, etc from the actual components.

Beobachten Sie jedes Tier: Sein Nervensystem ist auf seine Aufgaben zugeschnitten. Vögel haben ein scharfes Sehvermögen, um nach Nahrung und Bedrohungen zu suchen, und die Fähigkeit, ihre Haltung zu korrigieren und beim Sturz instinktiv Auftrieb zu erlangen. Und ein menschlicher Geist ist dazu verdrahtet, sich der Fähigkeiten seines eigenen Körpers, seines Gesundheitszustands, seines Hungers, seiner Kälte und der Anwesenheit oder Abwesenheit von Gliedmaßen sehr bewusst zu sein. Wenn wir RL als Grundlage für die Objektmodellierung verwenden, ist es kein Fehler, wenn der Verstand / die KI Aspekte wie Ort, Haltung, Gesundheit, gehaltene Gegenstände, Fähigkeiten usw. kennt. Was Sie könnten tun abstrakte Raketenwaffen behandelt werden als generischer Typ. Dies führt zu einer schönen abstrakten KI-Logik wieif holding missile weapon in hand, attempt to fire. Ein Gehirn in einem Glas, OTOH, ist genau das - vorausgesetzt, es weiß, dass es körperlos ist, weiß es auch, dass der Versuch, aus dem Glas herauszuklettern, vergeblich ist. Das heißt: Lassen Sie AI auf nullKomponenten prüfen und fahren Sie entsprechend fort.

I thought about diverging from the pure ECS pattern of keeping components as data containers only and actually implement those methods in the components themselves so that I can have a base AIControlledComponent

I thought I could refactor the code splitting it up based on the kind of enemy (aka an enemy that has a Weapon won't be able to Charge and vice versa) but then I'd have to have a base AIControlSystem and inherit from that and specialize it based on the components attached to the entities. This would result in pretty much one new system per enemy kind and it doesn't really look like a good solution.

  1. Es ist nicht das entscheidende Problem, sie als reine Datenelemente beizubehalten oder nicht. Aber Ihre Kontrollhierarchie ist, und in dieser Hinsicht wird der "reine" Ansatz oft als "gesunde Praxis" bezeichnet. In Wahrheit macht es kaum einen Unterschied, ob die Logik eingeschaltet ist thisoder auf etwas anderem (wie einem Controller höherer Ebene). Lassen Sie sich von der nicht reinen Methode nur nicht in Spaghetti verwandeln. (Meiner Erfahrung nach hat der reine Ansatz mehr monolithischen Code und macht es schwieriger, Fehler im lokalen Objekt zu machen, die nicht mit globalen Kontrollmechanismen übereinstimmen. Ich finde immer klare Top-Down-Ansätze am besten.)

  2. Einige werden anderer Meinung sein, aber im Allgemeinen ist die gesamte Idee hinter ECS Objektzusammensetzung vs. Vererbung. Ich sage nicht, dass Sie die Vererbung für einige Ihrer Komponententypen nicht verwenden konnten, aber ich würde sie so weit wie möglich vermeiden.

Based on the mental state, this systems adds a set of actions to a queue. Then the queue is being consumed and evaluated. An action like "Attack" is actually calling a method of the system that checks whether the entity has a WeaponComponent attached and fires, or if there's a ChargeComponent it charges at the player.

Ich denke, das ist von zentraler Bedeutung für Ihre Krise. Ihre KI-Geisteskomponente sollte, wenn sie den vollständigen Zustand ihres eigenen Körpers und der relevanten Teile der äußeren Umgebung kennt, niemals "Angriff" in die Warteschlange gestellt haben, es sei denn, die Umstände waren überhaupt ideal. Stellen Sie sich zum Beispiel vor, Sie hätten plötzlich gemerkt, dass Sie doch kein Gewehr in der Hand hatten, sondern ein Stiftmesser. Würden Sie immer noch angreifen und angreifen, besonders wenn der Feind alle mit Waffen bewaffnet wäre? Ich bezweifle das. Es ist taktisch sehr unterschiedlich und in Ihrem aktuellen Code wird diese Unterscheidung von Ihrer KI-Komponente nicht getroffen, bevor die betreffende Aktion in die Warteschlange gestellt wird.

whether the entity has a WeaponComponent attached and fires, or if there's a ChargeComponent it charges at the player

Das ist auch völlig falsch für mich. Warum ist Chargeein Component? Sicher ist es nur ein Verb, eine Art von Aktion, die jede KI ausführen kann, selbst wenn sie eine Raketenwaffe in der Hand hat? Sicherlich ist das einzige, was eine Ladung verhindern würde, Geschwindigkeit / Belastung, und dies sollten Faktoren sein, die bei der Entscheidung des KI-Geistes berücksichtigt werden, welche Maßnahmen auf der Grundlage des eigenen physischen Status des Unternehmens zu ergreifen sind. Aber ok.

Die letzten beiden Absätze geben Ihnen einen Überblick über Ihre Probleme: Sie verlassen die entscheidenden KI- Entscheidungen (!) Der ersten Klasse, die vor dem Anstehen getroffen werden, und werden in letzter Minute zu Entscheidungen zweiter Klasse, die in dem Moment getroffen werden, in dem Ihre Entität dies auch tun sollte den Abzug betätigen oder um sein Leben rennen. Klingt für mich nicht sehr entscheidend und ich kann mir vorstellen, was mit echten Soldaten passiert, die auf diese Weise kämpfen. Darüber hinaus bin ich mir nicht einmal sicher, ob die KI überhaupt in Ihre "zweite Vermutungs" -Phase involviert ist oder ob dies nur andere Komponenten sind, die diese endgültigen Entscheidungen darüber treffen, ob sie handeln sollen oder nicht ... wenn letztere müssen Sie dies in einphasige AI-Entscheidungsabwicklung ändern und Aktionen basierend auf Gewissheiten in die Warteschlange stellen.

Schlagen Sie vor, dass Sie Ihre KI-Verarbeitung vollständig konsolidieren, bevor diese Warteschlange beginnt. Die Probleme hier liegen alle in Ihrer KI-Strategie und nicht in Ihrer ECS-Nutzung, soweit ich sehen kann.


1

Verwenden Sie stattdessen sicher einen Verhaltensbaum. Jeder Verhaltensbaum wird spezifisch für den "Akteur". Jeder Akteur hat die Komponenten, die er hat, und da das BT daran angeschlossen ist, kennen die Knoten des BT die Komponenten und können sie abfragen. Jetzt weiß Ihr BT genau, was der Schauspieler zur Verfügung hat und kann damit arbeiten.

So haben Sie beispielsweise ein Vogel-BT, das Sie an Vogelwesen mit der Fliegenkomponente anhängen. Sie haben einen bestimmten fliegenden Blatt- / Aktionsknoten, den Sie in diesem BT verwenden, wenn Sie diese Vogel-KI herumbringen möchten. Da der Aktionsknoten fly ist, erwartet er, dass die Entität, an die er angehängt ist, die fly-Komponente hat. Es wird abfragen, um es zu erhalten, und wird damit arbeiten.

Jetzt können Ihre Entitäten, an die Komponenten und ein BT angehängt sind, Knoten in diesem BT haben, die an seinen Komponenten arbeiten.


Diese Antwort scheint davon auszugehen, dass der Leser bereits weiß, was Verhaltensbäume sind und wie sie funktionieren. Sie können es verbessern, indem Sie eine kurze Zusammenfassung geben oder zumindest einen Link zu einem veröffentlichen.
Philipp


Ich würde argumentieren, Sie sollten nicht unbedingt Komponenten innerhalb des BT mutieren, sondern "Tags" - oder "Marker" -Komponenten hinzufügen / entfernen, damit die Systeme (außerhalb des Baums) an der Entität arbeiten, die diese Marker / Tags enthält.
Seivan
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.