Ich arbeite an einem Entity-Komponentensystem in C ++, das hoffentlich dem Stil von Artemis (http://piemaster.net/2011/07/entity-component-artemis/) entspricht, da es sich bei den Komponenten hauptsächlich um Datentaschen handelt Systeme, die die Logik enthalten. Ich hoffe, dass ich die Datenzentrierung dieses Ansatzes nutzen und ein paar nette Content-Tools erstellen kann.
Ein Buckel, auf den ich stoße, ist jedoch, wie man einen Bezeichner-String oder eine GUID aus einer Datendatei nimmt und daraus eine Komponente für eine Entität erstellt. Offensichtlich könnte ich nur eine große Analysefunktion haben:
Component* ParseComponentType(const std::string &typeName)
{
if (typeName == "RenderComponent") {
return new RenderComponent();
}
else if (typeName == "TransformComponent") {
return new TransformComponent();
}
else {
return NULL:
}
}
Aber das ist wirklich hässlich. Ich beabsichtige, Komponenten häufig hinzuzufügen und zu ändern und hoffentlich eine Art ScriptedComponentComponent zu erstellen, sodass Sie eine Komponente und ein System in Lua zum Zwecke des Prototyping implementieren können. Ich möchte in der Lage sein, eine Klasse zu schreiben, die von einer Klasse erbt BaseComponent
, vielleicht ein paar Makros einzufügen, damit alles funktioniert, und dann die Klasse zur Laufzeit für die Instanziierung verfügbar zu haben.
In C # und Java wäre dies ziemlich einfach, da Sie nette Reflection-APIs zum Nachschlagen von Klassen und Konstruktoren erhalten. Aber ich mache das in C ++, weil ich meine Kenntnisse in dieser Sprache verbessern möchte.
Wie wird dies in C ++ erreicht? Ich habe über das Aktivieren von RTTI gelesen, aber es scheint, dass die meisten Leute diesbezüglich vorsichtig sind, insbesondere in einer Situation, in der ich es nur für eine Teilmenge von Objekttypen benötige. Wenn ich dort ein benutzerdefiniertes RTTI-System benötige, wo kann ich anfangen, das Schreiben eines Systems zu lernen?
quelle
Antworten:
Ein Kommentar:
Die Artemis-Implementierung ist interessant. Ich habe eine ähnliche Lösung gefunden, mit der Ausnahme, dass ich meine Komponenten "Attribute" und "Verhalten" nannte. Dieser Ansatz der Trennung von Komponententypen hat bei mir sehr gut funktioniert.
In Bezug auf die Lösung:
Der Code ist einfach zu verwenden, aber die Implementierung ist möglicherweise schwer zu befolgen, wenn Sie keine Erfahrung mit C ++ haben. So...
Die gewünschte Schnittstelle
Ich wollte ein zentrales Repository für alle Komponenten haben. Jeder Komponententyp ist einer bestimmten Zeichenfolge zugeordnet (die den Komponentennamen darstellt). So nutzen Sie das System:
Die Umsetzung
Die Implementierung ist nicht so schlecht, aber immer noch ziemlich komplex. Es erfordert einige Kenntnisse über Vorlagen und Funktionszeiger.
Anmerkung: Joe Wreschnig hat in den Kommentaren einige gute Punkte angesprochen, vor allem, wie meine vorherige Implementierung zu viele Annahmen darüber gemacht hat, wie gut der Compiler den Code optimiert. Das Problem war nicht schädlich, aber es hat mich auch gestört. Mir ist auch aufgefallen, dass das vorherige
COMPONENT_REGISTER
Makro mit Vorlagen nicht funktioniert hat.Ich habe den Code geändert und jetzt sollten alle diese Probleme behoben sein. Das Makro arbeitet mit Vorlagen und die Probleme, die Joe angesprochen hat, wurden behoben: Jetzt ist es für Compiler viel einfacher, unnötigen Code zu optimieren.
komponente / komponente.h
komponente / detail.h
component / component.cpp
Mit Lua erweitern
Ich sollte beachten, dass dies mit ein wenig Arbeit (es ist nicht sehr schwer) verwendet werden kann, um nahtlos mit Komponenten zu arbeiten, die entweder in C ++ oder Lua definiert sind, ohne jemals darüber nachdenken zu müssen.
quelle
shared_ptr
, aber Ihr Rat ist immer noch gut.Es scheint, als ob Sie eine Fabrik wollen.
http://en.wikipedia.org/wiki/Factory_method_pattern
Sie können Ihre verschiedenen Komponenten beim Hersteller registrieren lassen, welchem Namen sie entsprechen, und dann haben Sie eine Zuordnung der Zeichenfolgen-ID zur Signatur der Konstruktormethode, um Ihre Komponenten zu generieren.
quelle
Component
Klassen kennt und aufruftComponentSubclass::RegisterWithFactory()
, oder? Gibt es eine Möglichkeit, dies dynamischer und automatischer einzurichten? Der Arbeitsablauf, den ich suche, ist 1. Schreiben Sie eine Klasse und betrachten Sie nur den entsprechenden Header und die zugehörige CPP-Datei. 2. Kompilieren Sie das Spiel erneut.Ich habe eine Weile mit Paul Mantas Design aus der gewählten Antwort gearbeitet und bin schließlich zu dieser allgemeineren und prägnanteren Factory-Implementierung gekommen, die ich jedem mitteilen möchte, der sich in Zukunft mit dieser Frage befasst. In diesem Beispiel stammt jedes Factory-Objekt von der
Object
Basisklasse:Die statische Factory-Klasse lautet wie folgt:
Das Makro zum Registrieren eines Untertyps von
Object
lautet wie folgt:Jetzt ist die Verwendung wie folgt:
Die Kapazität für viele Zeichenfolgen-IDs pro Untertyp war in meiner Anwendung nützlich, aber die Beschränkung auf eine einzelne ID pro Untertyp wäre ziemlich einfach.
Ich hoffe das war hilfreich!
quelle
Aufbauend auf der Antwort von @TimStraubinger habe ich eine Factory-Klasse mit C ++ 14- Standards erstellt, in der abgeleitete Member mit einer beliebigen Anzahl von Argumenten gespeichert werden können . In meinem Beispiel wird im Gegensatz zu Tim nur ein Name / eine Taste pro Funktion verwendet. Wie Tims, wobei jede Klasse gespeichert ist , von einer abgeleiteten Basisklasse, Mine genannt wird Basis .
Base.h
EX_Factory.h
main.cpp
Ausgabe
Ich hoffe, dies hilft Menschen, die ein Factory- Design benötigen, für dessen Arbeit kein Identitätskonstruktor erforderlich ist. Das Entwerfen hat Spaß gemacht, daher hoffe ich, dass es den Menschen hilft, die mehr Flexibilität bei ihren Factory- Designs benötigen .
quelle