Derzeit stehe ich vor folgendem Problem:
Ich versuche, einen Pong-Klon mithilfe eines Entity-Component-Systems (ECS) zu schreiben . Ich habe das "Framework" ganz alleine geschrieben. Es gibt also eine Klasse, die die Entitäten mit allen Komponenten verwaltet. Dann gibt es die Komponentenklassen selbst. Und zuletzt gibt es meine Systeme, die nur alle Entitäten erhalten, die Komponenten haben, die das System benötigt.
So sucht mein Bewegungssystem beispielsweise nach allen Objekten, die eine Positionskomponente und eine Bewegungskomponente haben. Die Positionskomponente hält nur die Position und die Bewegungskomponente hält die Geschwindigkeit.
Das eigentliche Problem ist jedoch mein Kollisionssystem. Diese Klasse ist wie ein logischer Blob. Ich habe so viele Sonderfälle in dieser Klasse.
Zum Beispiel: Meine Paddel können mit den Rändern kollidieren. In diesem Fall wird ihre Geschwindigkeit auf Null gesetzt. Mein Ball kann auch mit den Rändern kollidieren. In diesem Fall wird die Geschwindigkeit jedoch nur auf der Normalen der Grenze gespiegelt, sodass sie reflektiert wird. Dazu habe ich dem Ball eine zusätzliche Physikkomponente gegeben, die nur sagt: "Hey, dieses Ding hört nicht auf, es reflektiert." Tatsächlich hat die Physikkomponente also keine realen Daten. Es ist eine leere Klasse, die nur dazu dient, dem System mitzuteilen, ob ein Objekt reflektiert oder stoppt.
Dann kommt Folgendes: Ich möchte einige Partikel rendern, wenn der Ball mit den Paddeln oder den Rändern kollidiert. Ich denke, der Ball muss eine weitere Komponente erhalten, die das Kollisionssystem anweist, bei einer Kollision Partikel zu erzeugen.
Dann möchte ich Power-Ups haben, die mit den Paddeln kollidieren können, aber nicht mit den Rändern. In diesem Fall müssen die Power-Ups verschwinden. Ich würde also viel mehr Fälle und Komponenten benötigen (um dem System mitzuteilen, dass einige Entitäten nur mit bestimmten anderen kollidieren können, aber nicht mit allen, selbst wenn andere tatsächlich kollidieren können, musste das Kollisionssystem außerdem die Power-Ups anwenden die Paddel usw. usw. usw. usw.).
Ich sehe, dass das Entitätskomponentensystem eine gute Sache ist, weil es flexibel ist und Sie keine Probleme mit der Vererbung haben. Aber ich stecke momentan total fest.
Denke ich zu kompliziert Wie soll ich mit diesem Problem umgehen?
Klar, ich muss Systeme erstellen, die tatsächlich für "Nachkollision" verantwortlich sind, also sagt das Kollisionssystem nur "Ja, wir haben eine Kollision im letzten Frame" und dann gibt es eine Reihe von "Nachkollisions" -Systemen, die Alle erfordern unterschiedliche (Kombinationen von) Komponenten und ändern dann die Komponenten. Zum Beispiel würde es ein Bewegungs-Nachkollisionssystem geben, das Dinge stoppt, die anhalten müssen, wenn eine Kollision auftritt. Dann ein Physik-Nachkollisionssystem, das Dinge usw. widerspiegelt.
Aber dies scheint mir auch keine richtige Lösung zu sein, weil zum Beispiel:
- Mein Bewegungsnachkollisionssystem würde Entitäten benötigen, die eine Positionskomponente, eine Bewegungskomponente und eine Kollisionskomponente haben. Dann würde es die Geschwindigkeit der Entität auf Null setzen.
- Das Physik-Nachkollisionssystem würde Entitäten benötigen, die eine Positionskomponente, eine Bewegungskomponente, eine Kollisionskomponente und eine Physikkomponente aufweisen. Dann würde es den Geschwindigkeitsvektor widerspiegeln.
Das Problem liegt auf der Hand: Die Bewegung nach der Kollision benötigt Entitäten, die eine Teilmenge der Entitäten im Physik-Nachkollisionssystem sind. Zwei Nachkollisionssysteme würden also mit denselben Daten arbeiten, was zur Folge hätte: Obwohl eine Entität eine physikalische Komponente hat, wäre ihre Geschwindigkeit nach einer Kollision Null.
Wie werden diese Probleme im Allgemeinen in einem Entitätskomponentensystem gelöst? Sind diese Probleme überhaupt üblich oder mache ich etwas falsch? Wenn ja, was und wie soll es stattdessen gemacht werden?
quelle
Sie machen die Dinge zu kompliziert. Ich würde sogar sagen, dass selbst die Verwendung von komponentenbasiertem Design für ein so einfaches Spiel einfach übertrieben ist. Mach die Dinge so, dass dein Spiel schnell und einfach zu entwickeln ist. Komponenten helfen bei der Iteration in größeren Projekten mit einer Vielzahl von Verhaltensweisen und Spielobjektkonfigurationen, aber ihr Nutzen für ein so einfaches, genau definiertes Spiel ist fraglicher. Ich habe letztes Jahr darüber gesprochen: Sie können in wenigen Stunden lustige kleine Spiele erstellen, wenn Sie sich darauf konzentrieren , ein Spiel zu erstellen , anstatt sich an eine Architektur zu halten . Die Vererbung bricht zusammen, wenn Sie 100 oder sogar 20 verschiedene Arten von Objekten haben, aber es funktioniert gut, wenn Sie nur eine Handvoll haben.
Angenommen, Sie möchten weiterhin Komponenten für Lernzwecke verwenden, gibt es einige offensichtliche Probleme mit Ihrem Ansatz, die auffallen.
Machen Sie Ihre Komponenten nicht so klein. Es gibt keinen Grund, feinkörnige Komponenten wie "Bewegung" zu haben. Es gibt keine generische Bewegung in Ihrem Spiel. Sie haben Paddel, deren Bewegung eng mit der Eingabe oder der KI verbunden ist (und verwenden Geschwindigkeit, Beschleunigung, Restitution usw. nicht wirklich), und Sie haben den Ball, der über einen genau definierten Bewegungsalgorithmus verfügt. Haben Sie einfach eine PaddleController-Komponente und eine BouncingBall-Komponente oder etwas in dieser Richtung. Wenn Sie ein komplizierteres Spiel erhalten, können Sie sich Sorgen über eine allgemeinere PhysicsBody-Komponente machen (die in "echten" Engines im Grunde nur eine Verbindung zwischen dem Spielobjekt und dem von Havok / PhysX / Bullet / verwendeten internen API-Objekt darstellt). Box2D / etc.), Die eine größere Vielfalt von Situationen bewältigt.
Sogar eine Positionskomponente ist fraglich, aber sicherlich nicht ungewöhnlich. Physik-Engines haben normalerweise ihre eigene interne Vorstellung davon, wo sich ein Objekt befindet, Grafiken haben möglicherweise eine interpolierte Darstellung und AI hat möglicherweise eine weitere Darstellung derselben Daten in einem anderen Zustand. Es kann vorteilhaft sein, einfach jedem System die Möglichkeit zu geben, seine eigene Vorstellung von der Transformation in den systemeigenen Komponenten zu verwalten und dann eine reibungslose Kommunikation zwischen den Systemen sicherzustellen. Weitere Informationen finden Sie im BitSquid-Blogbeitrag zu Ereignisströmen .
Denken Sie bei benutzerdefinierten Physik-Engines daran, dass Sie Daten zu Ihren Komponenten haben dürfen. Möglicherweise enthält eine generische Pong-Physikkomponente Daten, die angeben, auf welchen Achsen sie sich bewegen kann (z.
vec2(0,1)
B. als Multiplikator für Paddel, die sich nur auf der Y-Achsevec2(1,1)
bewegen können, und für den Ball, der angibt, dass sie sich bewegen kann), eine Flagge oder einen Schwimmer, die die Sprungkraft anzeigen (die Ball wäre in der Regel an1.0
und Paddel an0.0
), Beschleunigungseigenschaften, Geschwindigkeit usw. Der Versuch, dies in eine Unmenge verschiedener Mikrokomponenten für jedes hochverwandte Datenelement aufzuteilen, widerspricht dem, was ECS ursprünglich tun sollte. Behalten Sie Dinge, die zusammen verwendet werden, soweit möglich in derselben Komponente bei und teilen Sie sie nur dann auf, wenn es einen großen Unterschied gibt, wie jedes Spielobjekt diese Daten verwendet. Es gibt ein Argument dafür, dass für Pong die Physik zwischen dem Ball und den Paddeln unterschiedlich genug ist, um separate Komponenten zu sein, aber für ein größeres Spiel gibt es wenig Grund, 20 Komponenten zu erstellen, um das zu tun, was in 1-3 gut funktioniert.Denken Sie daran, wenn / wenn Ihre Version von ECS im Weg ist, tun Sie, was Sie brauchen, um Ihr Spiel tatsächlich zu machen, und vergessen Sie die hartnäckige Einhaltung eines Designmusters / einer Architektur.
quelle
Ball
, die die gesamte Logik für den Ball wie Bewegung, Hüpfen usw. enthält, und einePaddle
Komponente, die Eingaben entgegennimmt, aber das bin ich. Was für Sie am sinnvollsten ist und Ihnen aus dem Weg geht und Sie das Spiel machen lässt, ist der "richtige Weg", Dinge zu tun.Meiner Meinung nach *) ist Ihr größtes Problem mit Komponenten Folgendes: Komponenten sind NICHT hier, um anderen zu sagen , was zu tun ist. Komponenten sind hier, um Dinge zu tun. Sie haben keine Komponente, um nur den Speicher einer Sache zu speichern, und lassen dann andere Komponenten daran arbeiten. Sie möchten Komponenten, die mit den erhaltenen Daten arbeiten.
Wenn Sie sehen, wie Sie auf das Vorhandensein anderer Komponenten testen (und dort Funktionen aufrufen), ist dies ein klares Zeichen dafür, dass eines von zwei Dingen zutrifft:
Invalidate()
oderSetDirty()
rufen Sie andere Komponenten auf.Dies gilt übrigens für alle Arten von Systemen, nicht nur für Entity / Component-Systeme, sondern auch für die klassische Vererbung mit einfachen "GameObject" - oder sogar Bibliotheksfunktionen.
*) Wirklich nur meins . Die Meinungen über Whats Da Real Component Systemz (TM) sind sehr unterschiedlich.
quelle