Ich versuche, mich mit komponentenbasiertem Entity-Design zu beschäftigen.
Mein erster Schritt bestand darin, verschiedene Komponenten zu erstellen, die einem Objekt hinzugefügt werden konnten. Für jeden Komponententyp hatte ich einen Manager, der die Aktualisierungsfunktion jeder Komponente aufrief und bei Bedarf Dinge wie den Tastaturstatus usw. übergab.
Als nächstes habe ich das Objekt entfernt und jede Komponente mit einer ID versehen. Ein Objekt wird also durch Komponenten mit denselben IDs definiert.
Jetzt denke ich, dass ich keinen Manager für alle meine Komponenten benötige, zum Beispiel habe ich einen SizeComponent
, der nur eine Size
Eigenschaft hat). Infolgedessen verfügt der SizeComponent
über keine Aktualisierungsmethode, und die Aktualisierungsmethode des Managers führt keine Aktionen aus.
Mein erster Gedanke war, eine ObjectProperty
Klasse zu haben , die Komponenten abfragen kann, anstatt sie als Eigenschaften von Komponenten zu haben. Ein Objekt hätte also eine Anzahl von ObjectProperty
und ObjectComponent
. Komponenten verfügen über Aktualisierungslogik, die das Objekt nach Eigenschaften abfragt. Der Manager würde den Aufruf der Aktualisierungsmethode der Komponente verwalten.
Dies scheint mir eine Überentwicklung zu sein, aber ich glaube nicht, dass ich die Komponenten loswerden kann, da die Manager wissen müssen, welche Objekte welche Komponentenlogik benötigen, um ausgeführt zu werden (ansonsten würde ich die Komponente einfach entfernen vollständig und schieben Sie die Aktualisierungslogik in den Manager).
- Ist das (mit
ObjectProperty
,ObjectComponent
undComponentManager
Klassen) Over-Engineering? - Was wäre eine gute Alternative?
quelle
SizeComponent
ist übertrieben - man kann davon ausgehen, dass die meisten Objekte eine Größe haben - es sind Dinge wie Rendering, KI und Physik, bei denen das Komponentenmodell verwendet wird; Die Größe verhält sich immer gleich - so können Sie diesen Code freigeben.RenderingComponent
und a verwendet wirdPhysicsComponent
. Überlege ich mir, wo ich die Immobilie hinstellen soll? Sollte ich es in irgendein gerade haften, dann hat die andere Frage ein Objekt für die Komponente, die die erforderliche Eigenschaft hat?PhysicalStateInstance
(eine pro Objekt) neben einerGravityPhysicsShared
(eine pro Spiel); Ich bin jedoch versucht zu sagen, dass dies ein Versuch ist, sich in die Bereiche der Euphorie der Architekten zu wagen, und dass Sie sich nicht in ein Loch hineinversetzen (genau das, was ich mit meinem ersten Komponentensystem getan habe). KUSS.Antworten:
Die einfache Antwort auf Ihre erste Frage lautet: Ja, Sie haben das Design überarbeitet. Das "Wie weit kann ich die Dinge aufschlüsseln?" Frage ist sehr häufig, wenn der nächste Schritt unternommen wird und das zentrale Objekt (normalerweise eine Entität genannt) entfernt wird.
Wenn Sie die Objekte so detailliert aufteilen, dass sie eine eigene Größe haben, ist das Design zu weit gegangen. Ein Datenwert für sich ist keine Komponente. Es ist ein eingebauter Datentyp und kann oft genau so aufgerufen werden, wie Sie sie aufgerufen haben, eine Eigenschaft. Eine Eigenschaft ist keine Komponente, aber eine Komponente enthält Eigenschaften.
Hier sind einige Richtlinien, die ich bei der Entwicklung eines Komponentensystems zu befolgen versuche:
Da es sich bei der Richtlinie für Komponenten nicht um Strukturen handelt, wurde die SizeComponent-Komponente zu weit zerlegt. Es enthält nur Daten und was die Größe von etwas definiert, kann variieren. In einer Rendering-Komponente kann es sich beispielsweise um einen 1d-Skalar oder einen 2 / 3d-Vektor handeln. In einer Physikkomponente kann es sich um das Begrenzungsvolumen des Objekts handeln. In einem Inventarobjekt kann es sein, wie viel Platz es in einem 2D-Raster einnimmt.
Versuchen Sie, eine gute Grenze zwischen Theorie und Praxis zu ziehen.
Hoffe das hilft.
quelle
Sie haben bereits eine Antwort akzeptiert, aber hier ist mein Stab bei einem CBS. Ich stellte fest, dass eine generische
Component
Klasse einige Einschränkungen aufweist, und entschied mich für ein von Radical Entertainment auf der GDC 2009 beschriebenes Design, bei dem vorgeschlagen wurde, Komponenten inAttributes
und zu trennenBehaviors
. (" Theorie und Praxis der Architektur von Spielobjektkomponenten", Marcin Chady)Ich erkläre meine Entwurfsentscheidungen in einem zweiseitigen Dokument. Ich werde einfach den Link posten, da es zu lang ist, um alles hier einzufügen. Momentan werden nur die Logikkomponenten (nicht auch die Rendering- und Physikkomponenten) behandelt, aber es sollte Ihnen eine Vorstellung davon geben, was ich versucht habe:
► http://www.pdf-archive.com/2012/01/08/entity-component-system/preview/page/1
Hier ist ein Auszug aus dem Dokument:
Bearbeiten: Und hier ist ein Beziehungsdiagramm, das zeigt, wie die Komponenten miteinander kommunizieren:
Ein Implementierungsdetail: Die Entitätsebene
EventManager
wird nur erstellt, wenn sie verwendet wird. DieEntity
Klasse speichert nur einen Zeiger auf einenEventManager
, der nur dann initialisiert wird, wenn eine Komponente dies anfordert.Edit: Auf eine andere Frage habe ich eine ähnliche Antwort auf diese gegeben. Hier finden Sie möglicherweise eine bessere Erläuterung des Systems:
► /gamedev//a/23759/6188
quelle
Es hängt wirklich von den Eigenschaften ab, die Sie benötigen und wo Sie sie benötigen. Die Menge an Speicher, die Sie haben werden, und die Rechenleistung / den Typ, den Sie verwenden werden. Ich habe Folgendes gesehen und versuche es zu tun:
Diese Prinzipien haben ihre Grenzen. Natürlich müssen Sie die Geometrie auf den Renderer übertragen, aber Sie möchten sie wahrscheinlich nicht von dort abrufen. Die Master-Geometrie wird in der Physik-Engine gespeichert, falls dort Deformationen auftreten, und mit dem Renderer synchronisiert (von Zeit zu Zeit abhängig von der Entfernung der Objekte). In gewisser Weise werden Sie es also trotzdem duplizieren.
Es gibt keine perfekten Systeme. Und einige Spiele sind mit einem einfacheren System besser dran, während andere komplexere systemübergreifende Synchronisationen erfordern.
Stellen Sie zunächst sicher, dass auf alle Eigenschaften von Ihren Komponenten aus auf einfache Weise zugegriffen werden kann, damit Sie die Art und Weise ändern können, in der Sie die Eigenschaften transparent speichern, sobald Sie mit der Feinabstimmung Ihrer Systeme beginnen.
Es ist keine Schande, einige Eigenschaften zu kopieren. Wenn einige Komponenten eine lokale Kopie enthalten müssen, ist das Kopieren und Synchronisieren manchmal effizienter als der Zugriff auf einen "externen" Wert.
Das Synchronisieren muss auch nicht bei jedem Frame erfolgen. Einige Komponenten können seltener synchronisiert werden als andere. Render-Komponenten sind oft ein gutes Beispiel. Diejenigen, die nicht mit den Spielern interagieren, können weniger häufig synchronisiert werden, genauso wie diejenigen, die weit entfernt sind. Die weit und außerhalb des Kamerafeldes können noch seltener synchronisiert werden.
Wenn es um Ihre Größenkomponente geht, könnte diese wahrscheinlich in Ihrer Positionskomponente gebündelt sein:
Anstelle der Größe können Sie auch einen Größenmodifikator verwenden, der möglicherweise einfacher zu handhaben ist.
Bezüglich der Speicherung aller Eigenschaften in einem generischen Eigenschaftenspeichersystem ... Ich bin mir nicht sicher, ob Sie in die richtige Richtung gehen ... Konzentrieren Sie sich auf die Eigenschaften, die für Ihr Spiel von zentraler Bedeutung sind, und erstellen Sie Komponenten, die so viele verwandte Eigenschaften wie möglich bündeln. Solange Sie den Zugriff auf diese Eigenschaften ordnungsgemäß abstrahieren (z. B. durch Getter in den Komponenten, die sie benötigen), sollten Sie sie später verschieben, kopieren und synchronisieren können, ohne die Logik zu stark zu beeinträchtigen.
quelle
Wenn Komponenten willkürlich zu Entitäten hinzugefügt werden können, müssen Sie die Möglichkeit haben, abzufragen, ob eine bestimmte Komponente in einer Entität vorhanden ist, und einen Verweis darauf abzurufen. Sie können also eine von ObjectComponent abgeleitete Liste von Objekten durchlaufen, bis Sie die gewünschte gefunden und zurückgegeben haben. Sie würden jedoch ein Objekt des richtigen Typs zurückgeben.
In C ++ oder C # bedeutet dies normalerweise, dass Sie eine Template-Methode für die Entität haben, wie
T GetComponent<T>()
. Und sobald Sie diese Referenz haben, wissen Sie genau, welche Mitgliedsdaten darin enthalten sind. Greifen Sie einfach direkt darauf zu.In etwas wie Lua oder Python haben Sie nicht unbedingt einen expliziten Typ dieses Objekts, und das ist Ihnen wahrscheinlich auch egal. Aber auch hier können Sie einfach auf die Mitgliedsvariable zugreifen und alle Ausnahmen behandeln, die beim Versuch auftreten, auf etwas zuzugreifen, das nicht vorhanden ist.
Das Abfragen von Objekteigenschaften klingt explizit so, als ob Sie die Arbeit duplizieren würden, die die Sprache für Sie leisten kann, entweder zur Kompilierungszeit für statisch typisierte Sprachen oder zur Laufzeit für dynamisch typisierte Sprachen.
quelle
Die Sache ist, dass RenderingComponent die Position verwendet, aber die PhysicsComponent sie bereitstellt . Sie müssen nur jedem Benutzer mitteilen, welchen Anbieter er verwenden soll. Im Idealfall agnostisch, sonst besteht eine Abhängigkeit.
Es gibt keine gemeinsame Regel. Hängt von der jeweiligen Immobilie ab (siehe oben).
Erstelle ein Spiel mit einer hässlichen, aber komponentenbasierten Architektur und überarbeite es dann.
quelle
PhysicsComponent
ich dann tun soll. Ich betrachte es als das Simulieren des Objekts in einer physischen Umgebung, was mich zu dieser Verwirrung führt: Nicht alle Dinge, die gerendert werden müssen, müssen simuliert werden, daher erscheint es mir falsch, sie hinzuzufügen,PhysicsComponent
wenn ich sie hinzufüge,RenderingComponent
weil sie eine Position enthalten dasRenderingComponent
nutzt. Ich könnte mir leicht vorstellen, mit einem Netz von miteinander verbundenen Komponenten zu enden, was bedeutet, dass alle / die meisten zu jeder Entität hinzugefügt werden müssen.Dein Bauch sagt dir, dass du das
ThingProperty
,ThingComponent
undThingManager
für jedeThing
Art von Komponente einen kleinen Overkill hast . Ich denke es ist richtig.Sie müssen jedoch eine Möglichkeit haben, die verwandten Komponenten im Hinblick darauf zu verfolgen, welche Systeme sie verwenden, zu welcher Entität sie gehören usw.
TransformProperty
wird ziemlich verbreitet sein. Aber wer ist dafür verantwortlich, das Rendering-System? Das physikalische System? Das Soundsystem? Warum muss sich eineTransform
Komponente selbst aktualisieren?Die Lösung besteht darin, jede Art von Code außerhalb von Gettern, Setzern und Initialisierern aus Ihren Eigenschaften zu entfernen. Komponenten sind Daten, die von Systemen im Spiel verwendet werden, um verschiedene Aufgaben wie Rendern, KI, Soundwiedergabe, Bewegung usw. auszuführen.
Lesen Sie mehr über Artemis: http://piemaster.net/2011/07/entity-component-artemis/
Wenn Sie sich den Code ansehen, werden Sie feststellen, dass er auf Systemen basiert, deren Abhängigkeiten als Listen von deklariert werden
ComponentTypes
. Sie schreiben jede IhrerSystem
Klassen und deklarieren in der Methode constructor / init, von welchen Typen das System abhängt.Während der Einrichtung von Ebenen oder Ähnlichem erstellen Sie Ihre Entitäten und fügen ihnen Komponenten hinzu. Anschließend weisen Sie diese Entität an, Artemis Bericht zu erstatten, und Artemis ermittelt anhand der Zusammensetzung dieser Entität, welche Systeme über diese Entität informiert werden möchten.
Während der Aktualisierungsphase Ihrer Schleife haben Sie
System
nun eine Liste der zu aktualisierenden Entitäten. Jetzt können Sie Granularität der Komponenten, so dass Sie verrückt Systeme entwickeln können, bauen Einheiten aus aModelComponent
,TransformComponent
,FliesLikeSupermanComponent
, undSocketInfoComponent
, und tun Sie etwas seltsam wie machen eine fliegende Untertasse , dass Fliegen zwischen den Kunden zu einem Multiplayer - Spiel verbunden. Okay, vielleicht nicht, aber die Idee ist, dass die Dinge entkoppelt und flexibel bleiben.Artemis ist nicht perfekt und die Beispiele auf der Website sind ein wenig grundlegend, aber die Trennung von Code und Daten ist stark. Es ist auch gut für Ihren Cache, wenn Sie es richtig machen. Artemis macht das wahrscheinlich nicht richtig, aber es ist gut, daraus zu lernen.
quelle