Ich werde aus ein wenig Erfahrung sprechen, von einem starren OO-Design zu einem Entity-Component-System (ECS) -Design.
Vor einiger Zeit war ich genau wie Sie , ich hatte eine Reihe verschiedener Arten von Dingen, die ähnliche Eigenschaften hatten, und ich baute verschiedene Objekte aus und versuchte, sie mithilfe der Vererbung zu lösen. Eine sehr kluge Person sagte mir , dass ich das nicht tun und stattdessen Entity-Component-System verwenden soll.
Jetzt ist ECS ein großes Konzept, und es ist schwierig, es richtig zu machen. Es steckt viel Arbeit dahinter, Entitäten, Komponenten und Systeme richtig aufzubauen. Bevor wir dies jedoch tun können, müssen wir die Begriffe definieren.
- Entität : Dies ist das Ding , der Spieler, das Tier, der NPC, was auch immer . Es ist eine Sache, an die Komponenten angeschlossen werden müssen.
- Komponente : Dies ist das Attribut oder die Eigenschaft , z. B. "Name" oder "Alter" oder "Eltern" in Ihrem Fall.
- System : Dies ist die Logik hinter einer Komponente oder einem Verhalten . Normalerweise erstellen Sie ein System pro Komponente, dies ist jedoch nicht immer möglich. Außerdem müssen Systeme manchmal andere Systeme beeinflussen.
Also, hier ist, wohin ich damit gehen würde:
Erstellen ID
Sie in erster Linie eine für Ihre Charaktere. Ein int
, Guid
, was Sie wollen. Dies ist die "Entität".
Zweitens sollten Sie über die verschiedenen Verhaltensweisen nachdenken, die Sie haben. Dinge wie der "Stammbaum" - das ist ein Verhalten. Erstellen Sie ein System, das alle diese Informationen enthält, anstatt dies als Attribute für die Entität zu modellieren . Das System kann dann entscheiden, was damit zu tun ist.
Ebenso wollen wir ein System für "Ist der Charakter lebendig oder tot?" Dies ist eines der wichtigsten Systeme in Ihrem Design, da es alle anderen beeinflusst. Einige Systeme können die "toten" Zeichen löschen (z. B. das "Sprite" -System), andere Systeme können die Dinge intern neu anordnen, um den neuen Status besser zu unterstützen.
Sie bauen beispielsweise ein "Sprite" - oder "Drawing" - oder "Rendering" -System auf. Dieses System hat die Verantwortung zu bestimmen, mit welchem Sprite der Charakter angezeigt werden soll und wie er angezeigt werden soll. Wenn ein Charakter stirbt, entferne ihn.
Zusätzlich ein "KI" -System, das einem Charakter sagen kann, was zu tun ist, wohin er gehen soll usw. Dies sollte mit vielen anderen Systemen interagieren und basierend auf diesen Entscheidungen treffen. Auch hier können tote Charaktere wahrscheinlich aus diesem System entfernt werden, da sie eigentlich nichts mehr tun.
Ihr "Name" -System und Ihr "Family Tree" -System sollten den Charakter (lebendig oder tot) wahrscheinlich im Speicher behalten. Dieses System muss diese Informationen abrufen, unabhängig vom Status des Charakters. (Jim ist immer noch Jim, auch nachdem wir ihn begraben haben.)
Dies bietet Ihnen auch den Vorteil einer Änderung, wenn ein System effizienter reagiert : Das System verfügt über einen eigenen Timer. Einige Systeme müssen schnell ausgelöst werden, andere nicht. Hier beginnen wir mit dem, was ein Spiel effizient laufen lässt. Wir müssen das Wetter nicht jede Millisekunde neu berechnen, wir können das wahrscheinlich alle 5 oder so tun.
Es gibt Ihnen auch mehr kreative Hebelwirkung: Sie können ein "Pathfinder" -System erstellen, das die Berechnung eines Pfades von A nach B übernimmt und bei Bedarf aktualisiert werden kann, sodass das Bewegungssystem sagen kann, "wo muss ich hin" als nächstes gehen? " Wir können diese Bedenken jetzt vollständig trennen und effektiver darüber nachdenken. Bewegung muss nicht den Weg finden, sondern nur dorthin gelangen.
Sie sollten einige Teile eines Systems nach außen aussetzen. In Ihrem Pathfinder
System möchten Sie wahrscheinlich eine Vector2 NextPosition(int entity)
. Auf diese Weise können Sie diese Elemente in streng kontrollierten Arrays oder Listen aufbewahren. Sie können kleinere struct
Typen verwenden, mit denen Sie Komponenten in kleineren, zusammenhängenden Speicherblöcken aufbewahren können, wodurch Systemaktualisierungen viel schneller durchgeführt werden können. (Insbesondere wenn die externen Einflüsse auf ein System minimal sind, muss es sich nur noch um seinen internen Zustand kümmern, wie z Name
.
Aber, und ich kann das nicht genug betonen, jetzt Entity
ist es nur ein ID
, einschließlich Kacheln, Objekte usw. Wenn eine Entität nicht zu einem System gehört, wird das System sie nicht verfolgen. Dies bedeutet, dass wir unsere "Baum" -Objekte erstellen, sie in den Systemen Sprite
und speichern können Movement
(die Bäume bewegen sich nicht, aber sie haben eine "Position" -Komponente) und sie von den anderen Systemen fernhalten können. Wir brauchen keine spezielle Liste mehr für Bäume, da das Rendern eines Baumes abgesehen von Paperdolling nicht anders ist als ein Charakter. (Was das Sprite
System steuern kann oder was das System steuern Paperdoll
kann.) Jetzt NextPosition
kann unser leicht umgeschrieben werden: Vector2? NextPosition(int entity)
und es kann eine null
Position für Entitäten zurückgeben, die es nicht interessiert. Wir wenden dies auch auf unsere an NameSystem.GetName(int entity)
, es kehrt null
für Bäume und Felsen zurück.
Ich werde dies zu Ende bringen, aber die Idee hier ist, Ihnen einige Hintergrundinformationen zu ECS zu geben und wie Sie es wirklich nutzen können, um Ihnen ein besseres Design für Ihr Spiel zu geben. Sie können die Leistung steigern, nicht verwandte Elemente entkoppeln und die Dinge besser organisieren. (Dies passt auch gut zu funktionalen Sprachen / Setups wie F # und LINQ. Ich empfehle dringend, F # zu überprüfen, wenn Sie dies noch nicht getan haben. Es passt sehr gut zu C #, wenn Sie sie zusammen verwenden.)