Viele Bewegungsquellen in einem Entitätssystem

9

Ich bin ziemlich neu in der Idee von Entitätssystemen, nachdem ich eine Menge Dinge gelesen habe (am nützlichsten dieser großartige Blog und diese Antwort ).

Obwohl ich ein wenig Probleme habe zu verstehen, wie einfach es ist, die Position eines Objekts durch eine undefinierte Anzahl von Quellen zu manipulieren.

Das heißt, ich habe meine Entität, die eine Positionskomponente hat. Ich habe dann ein Ereignis im Spiel, das dieser Entität sagt, dass sie sich in einer bestimmten Zeit um eine bestimmte Strecke bewegen soll.

Diese Ereignisse können jederzeit auftreten und haben unterschiedliche Werte für Position und Zeit. Das Ergebnis ist, dass sie zusammengesetzt würden.

In einer herkömmlichen OO-Lösung hätte ich eine Art MoveByKlasse, die die Entfernung / Zeit und eine Reihe von Klassen in meiner Spielobjektklasse enthält. Bei jedem Frame würde ich alle durchlaufen MoveByund auf die Position anwenden. Wenn a MoveByseine Endzeit erreicht hat, entfernen Sie es aus dem Array.

Mit dem Entitätssystem bin ich ein wenig verwirrt darüber, wie ich diese Art von Verhalten replizieren soll.

Wenn es nur eine davon zu einer Zeit gäbe, anstatt sie zusammenzusetzen, wäre es ziemlich einfach (glaube ich) und würde ungefähr so ​​aussehen:

PositionComponent enthält x, y

MoveByComponent enthält x, y, time

Entitywelches sowohl a PositionComponentals auch a hatMoveByComponent

MoveBySystemDas sucht nach einer Entität mit diesen beiden Komponenten und addiert den Wert von MoveByComponentzu PositionComponent. Wenn das timeerreicht ist, wird die Komponente aus dieser Entität entfernt.

Ich bin ein bisschen verwirrt darüber, wie ich das gleiche mit vielen Moves machen würde.

Meine ersten Gedanken sind, dass ich hätte:

PositionComponent, MoveByComponentDie gleichen wie oben

MoveByCollectionComponentwelches ein Array von MoveByComponents enthält

MoveByCollectionSystemdas sucht nach einer Entität mit a PositionComponentund a MoveByCollectionComponent, die durch das MoveByComponents darin iteriert und nach Bedarf anwendet / entfernt.

Ich denke, dies ist ein allgemeineres Problem, viele der gleichen Komponenten zu haben und zu wollen, dass ein entsprechendes System auf jede einzelne einwirkt. Meine Entitäten enthalten ihre Komponenten in einem Hash vom Komponententyp -> Komponente, haben also streng genommen nur 1 Komponente eines bestimmten Typs pro Entität.

  1. Ist das der richtige Weg, dies zu betrachten?

  2. Sollte eine Entität immer nur eine Komponente eines bestimmten Typs haben?

Klebrig
quelle
1
Klingt so, als wäre die MoveByFunktionalität nur eine Geschwindigkeit? Es hört sich so an, als wären Sie auf dem richtigen Weg. Für Ihre zweite Frage gibt es viele verschiedene Implementierungen von Entitäts- / Komponentensystemen. Die in meiner Antwort, die Sie verlinkt haben, beschriebene Beschreibung enthält nur eine Komponente eines bestimmten Typs.
MichaelHouse
Irgendwie, aber der Unterschied besteht darin, dass diese Geschwindigkeit nur von einer Zeit zur anderen gültig ist und viele von ihnen gleichzeitig zusammengesetzt werden können. Ich glaube, ich brauchte nur eine gewisse Bestätigung, ich war in der Vergangenheit streng (anal, fast) OO für meine Spiele - was Jahre später im selben Projekt unsere Produktionsgeschwindigkeit beeinträchtigt hat - und dies ist ein beängstigend unbekanntes Gebiet;) . Tolle Antwort auf den anderen Beitrag übrigens, half einige Dinge zu klären
Sticky
Ich mache es so: Ich habe PlayerInputComponent und AIInputComponent (oder Systeme), die MobileBehaviorComponent mitteilen, dass MobileBehaviorComponent beim Klicken auf die Tastatur oder bei der KI, dass sich das Handy irgendwo bewegen soll, speichert, dass es sich irgendwo bewegen soll (FSM für mobile Aktionen enthält). und ein System wird es bewegen. Ihre Granularität ist einfach zu groß. Bei übergeordneten Komponenten wie Transform, Model, Light, Mob funktioniert alles genauso gut. Außerdem musste ich nie Komponenten entfernen - ich denke, sie ähneln eher etwas, das ein Spielobjekt beschreibt, damit es nicht einfach verschwinden kann.
Kikaimaru
Dieses spezielle MoveBy-Beispiel war nur ein Beispiel. Die Frage war eher, wie man solche Dinge zusammensetzt. Wenn ich speziell sagen muss: "In 5 Sekunden um x = 5 und y = 6 bewegen", "In 10 Sekunden um x = 10 y = 2 bewegen", würde ich das gleichzeitig tun?
Sticky
Was meinst du mit "zusammengesetzt"? Möchten Sie Geschwindigkeiten hinzufügen? Wenn Sie also zusammengesetzt wären move x by 10 in 2 secondsund move x by -10 in 2 secondsdas Unternehmen vollkommen still stehen würde?
Tom Dalling

Antworten:

6

Für Ihr Szenario fügen wir einem Spielobjekt normalerweise drei Komponenten hinzu:

  1. TransformComponent (Position, Ausrichtung, Skalierung)
  2. VelocityComponent (Geschwindigkeit, Richtung)
  3. ControllerComponent

Wenn Spielobjekte eine Art KI-Funktionalität benötigen, z. B. das Bewegen auf einem von Ihnen beschriebenen Pfad, weisen wir der Liste der Komponenten einen AIController zu. AIController sind wirklich nichts weiter als ein Wrapper, der einen Verhaltensbaum erstellt. Im Verhaltensbaum entwerfen wir die eigentliche Funktionalität, die das Spielobjekt ausführen soll, z. B.:

BehaviorTree* tree(new SequentialNode());
tree->addChild(new MoveToNode(x,y,z));
tree->addChild(new WaitNode(30));
tree->addChild(new MoveToNode(a,b,c));
tree->addChild(new WaitNode(30));
gameObject->addComponent(new AIController(tree));

Das AI-Subsystem verwaltet AIController, sodass das Subsystem den Controller ankreuzt, der wiederum den Verhaltensbaum ausführt. Der MoveToNode () betrachtet die aktuelle Position / Ausrichtung, berechnet anhand der Konstruktorargumente einen Richtungsvektor und die Geschwindigkeit, zu der Sie sich bewegen möchten, und legt die Werte für die Geschwindigkeitskomponente fest. Das Bewegungssystem ist dafür verantwortlich, Bewegungskomponenten mit Werten zu lesen und Physik anzuwenden, wodurch die Position / Orientierung entsprechend aktualisiert wird.

Der obige Code verschiebt einfach ein Spielobjekt vom Spawn-Ort nach x, y, z im Weltraum, wartet dann mindestens 30 Sekunden, verschiebt das Spielobjekt dann zum Ort a, b, c und wartet dann weitere 30 Sekunden. Sobald das Warten beendet ist, ist die Verhaltenssequenz beendet und wird von Anfang an wiederholt.

Auf diese Weise können Sie auf einfache Weise definieren, welche AI-Funktionen Sie benötigen, die alle im AI-Subsystem enthalten sind, ohne dass dies Auswirkungen auf Ihr Entity-Subsystem hat. Außerdem können Sie die Liste Ihrer Entitätssystemkomponenten schlank halten, ohne zu viel Granularität zu haben.

Naros
quelle
1

Eine Option besteht darin, Ihrem Design Controller hinzuzufügen. Entitäten besitzen Daten zur Darstellung der Position (im Fall meiner Engine haben sie Daten, die sich auch an die vorherigen Positionen erinnern, damit ich den Geschwindigkeitsvektor kennen kann und ob sie bewegt oder teleportiert werden), aber sie wissen nichts über Physik oder AI. Controller verschieben Entitäten, und Sie können mehrere Controller haben, die dieselbe Entität betreffen, oder einen Controller, der verschiedene Entitäten betrifft.

Beispiel: Erstellen Sie eine Basis-Controller-Klasse mit einer run () -Methode, oder wenn Ihnen der Name nicht gefällt, rufen Sie ihn think (), update () oder tick () auf. Dann erben Sie davon und erstellen einen MoveController, NPCController, PlayerInputController (für die Player-Entität), PhysicController; Dann implementieren Sie die run () -Methode. Ich würde Ihre MoveByComponent in den MoveController und nicht in Entity einfügen.

Diese Controller können von jeder Entität instanziiert werden, wenn sie spezifische Daten einer Entität enthalten. Sie können zerstört oder zur Wiederverwendung zurückgesetzt werden. Sie können einen Controller auch zum Verschieben einer Gruppe von Entitäten verwenden. Wenn Sie beispielsweise in einem RTE-Spiel verschiedene Einheiten als Gruppe verschieben müssen und ein Controller von jeder Einheit die Spielleistung beeinträchtigen kann, können Sie einfach alle Einheiten zuweisen zu einem GroupController oder LegionController und lassen Sie ihn die Einheiten als Teil einer organisierten Gruppe verschieben. Wenn das Spiel im Kampf das Verhalten einzelner Einheiten zulässt und wahrscheinlich die meisten Spiele dies tun, müssen Sie zu einem UnitController wechseln, aber es ist besser, dies nur bei Bedarf zu tun, als von Anfang an.

In meinem Entwicklungsspiel habe ich einen MoveController, der Entitäten einem Pfad folgt. Für jeden NPC und den Spielercharakter gibt es einen MoveController. Gelegentlich wird eine für Kisten oder Steine ​​erstellt, die der Spieler schieben kann. Der PhysicController, nur eine Instanz, die die Positionen aller ihm zugewiesenen Entitäten überprüft. Wenn eine Entität mit einer anderen zugewiesenen Entität kollidiert, wird die resultierende Position von beiden berechnet (er macht tatsächlich mehr als das, aber Sie haben die Idee). Der NPCController ist die KI, eine Instanz pro NPC. Es überprüft die Situation des NPC und entscheidet, wohin es sich bewegen soll. Anschließend wird der Pfad zu einem MoveController verschoben, der den NPC tatsächlich bewegt. Controller haben eine Priorität, daher kann ich im Voraus ihre Reihenfolge bestimmen. Der PhysicController ist der letzte, der ausgeführt wird.

Ich befürworte Controller, ist aber nicht die einzige "richtige" Option. Ich erinnere mich beispielsweise an eine Entity-Schnittstelle in der Cafu-Engine, die die think () -Methode in der Entity selbst hat. Der Benutzer der Klasse muss von Entity erben und think () implementieren. Ich erinnere mich an eine abgeleitete Klasse namens CompanyBot (die mit dem Beispiel geliefert wird) Spiel), die bei dieser Methode eine Kollisionsprüfung durchführen, da sie als "denken" bezeichnet wird, können wir davon ausgehen, dass AI-Code ebenfalls vorhanden sein wird. Während die NeoAxis-Engine (das letzte Mal, als ich sie mir angesehen habe) KI und Physik von den Entitäten getrennt hat.

Es gibt ein Controller-Muster, das ich gehört habe. Vielleicht sollten Sie danach suchen, und das ist wahrscheinlich nicht genau das, worüber ich hier spreche, aber es klingt auch nach einer guten Lösung.

Hatoru Hansou
quelle
Das ist im Grunde das OO-Design, das wir bereits jetzt haben. Als Entität mit Derivaten (Charakter, Monster) usw. habe ich ein Team von uns geführt, das seit fast 2 Jahren Vollzeit an diesem Spiel arbeitet, und da jeder die Dinge nach Belieben ändert, ist es schrecklich, schrecklich geworden Codebasis - und es dauert peinlich lange, bis neue Funktionen ausgeliefert werden. Die Entity System-Idee scheint genau das zu sein , wonach ich suche. Obwohl Ihre Antwort nicht ganz relevant ist, sollten Sie die Links oben in der Frage selbst nachlesen, um zu sehen, ob sie Ihnen helfen können :)
Sticky
@Sticky Ich muss zugeben, dass das Node System plus Entity aus Komponenten eine clevere Art ist, die verschiedenen benötigten Systeme darzustellen, als mein vorgeschlagener Controller-Ansatz, der einer weniger entwickelten Version ähnelt. Du brauchst meine Antwort doch wirklich nicht.
Hatoru Hansou
Keine Bange. Der OO-Weg hat seine Vorteile, aber die Dinge werden hässlich, schnell
Sticky