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 MoveBy
Klasse, die die Entfernung / Zeit und eine Reihe von Klassen in meiner Spielobjektklasse enthält. Bei jedem Frame würde ich alle durchlaufen MoveBy
und auf die Position anwenden. Wenn a MoveBy
seine 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
Entity
welches sowohl a PositionComponent
als auch a hatMoveByComponent
MoveBySystem
Das sucht nach einer Entität mit diesen beiden Komponenten und addiert den Wert von MoveByComponent
zu PositionComponent
. Wenn das time
erreicht 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
, MoveByComponent
Die gleichen wie oben
MoveByCollectionComponent
welches ein Array von MoveByComponent
s enthält
MoveByCollectionSystem
das sucht nach einer Entität mit a PositionComponent
und a MoveByCollectionComponent
, die durch das MoveByComponent
s 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.
Ist das der richtige Weg, dies zu betrachten?
Sollte eine Entität immer nur eine Komponente eines bestimmten Typs haben?
MoveBy
Funktionalitä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.move x by 10 in 2 seconds
undmove x by -10 in 2 seconds
das Unternehmen vollkommen still stehen würde?Antworten:
Für Ihr Szenario fügen wir einem Spielobjekt normalerweise drei Komponenten hinzu:
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.:
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.
quelle
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.
quelle