Ich versuche, ein komponentenbasiertes Entitätssystem zu implementieren, bin aber etwas verwirrt darüber, wie ich mit dem Messaging umgehen soll. Es gibt zwei Probleme, die ich lösen möchte, damit ich das System testen kann. Unten ist der Code, den ich bisher habe,
Die Entitätsklasse:
class Entity{
public:
Entity(unsigned int id):
id_(id)
{};
void handleMessage(BaseMessage &message){
for(auto element: components_){
element.second->handleMessage(message);
}
}
template<class T>
void attachComponent(T *component){
//Consider making safer in case someone tries to attach same component type twice
components_[typeid(T).hash_code()] = component;
}
template<class T>
void detachComponent(void){
components_.erase(typeid(T).hash_code());
}
template<class T>
T* getComponent(void)const{
return *components_.find(typeid(T).hash_code());
}
unsigned int getInstanceID(void)const{
return id_;
}
private:
unsigned int id_;
std::map<size_t, BaseComponent*> components_;
};
Die Klassen Basiskomponente und Nachricht:
class BaseComponent{
public:
virtual void handleMessage(BaseMessage &message){};
};
class BaseMessage{
public:
virtual int getType(void) = 0;
};
1. Behandlung von Nachrichtentypen
Meine erste Frage ist, wie ich mit den verschiedenen (von BaseMessage abgeleiteten) Nachrichtentypen umgehen soll.
Ich habe mir zwei Möglichkeiten ausgedacht, um mit den Nachrichtentypen der abgeleiteten Nachrichtentypen umzugehen. Eine besteht darin, einen Hash (dh unter Verwendung von FNV) aus einer Zeichenfolge zu generieren, die den Nachrichtentyp benennt, und diesen Hash zu verwenden, um den Nachrichtentyp zu bestimmen. Die handleMessage(BaseMessage &message)
Funktion würde also zuerst diesen Hash aus der Nachricht extrahieren und dann einen static_cast für den entsprechenden Typ ausführen.
Die zweite Methode besteht darin, eine Vorlage wie folgt zu verwenden (ähnlich den attachComponent
Methoden der Entitätsklasse):
template<class T>
handleMessage(T& message){};
und Spezialisierungen für jeden Nachrichtentyp vornehmen, den die jeweilige Komponente vornehmen wird.
Gibt es irgendwelche Nachteile bei der zweiten Methode? Was ist mit der Leistung? Warum sehe ich diese Art der Verwendung nicht öfter?
2. Eingabehandhabung
Meine zweite Frage ist, wie (in Bezug auf Latenz und Benutzerfreundlichkeit) Eingaben optimal verarbeitet werden können.
Mein Gedanke war, eine zu erstellen, InputHandlerComponent
die sich bei der Tastaturklasse registriert, um bestimmte Tastendrücke zu hören, die möglicherweise in einer Datei definiert sind. Zum Beispiel
keyboard.register( player.getComponent<InputHandler>() , 'W')
Ich wünschte, es gäbe eine präzisere Anleitung für komponentenbasierte Systeme, aber ich denke, es gibt viele verschiedene Möglichkeiten, die gleichen Dinge zu tun. Ich habe weitere Fragen, aber ich denke, es wäre klüger, zuerst zu versuchen, das zu implementieren, was ich kann.
quelle
typeid
(siehe Component und ComponentContainer). Grundsätzlich speichere ich den "Typ" einer Komponente als Ganzzahl. Ich habe eine globale Ganzzahl, die ich pro Komponententyp inkrementiere. Und ich speichere Komponenten in einem 2d-Array, wobei der erste Index die ID der Entität und der zweite Index die ID des Komponententyps ist, dh Komponenten [entityId] [componentTypeId]Ich arbeite an einem komponentenbasierten Entitätssystem in C #, aber die allgemeinen Ideen und Muster gelten weiterhin.
Ich gehe mit Nachrichtentypen um, indem jede Komponentenunterklasse die geschützte
RequestMessage<T>(Action<T> action) where T : IMessage
Methode der Komponente aufruft . Im Englischen bedeutet dies, dass die Komponentenunterklasse einen bestimmten Nachrichtentyp anfordert und eine Methode bereitstellt, die aufgerufen werden kann, wenn die Komponente eine Nachricht dieses Typs empfängt.Diese Methode wird gespeichert, und wenn die Komponente eine Nachricht empfängt, verwendet sie Reflection, um den Nachrichtentyp abzurufen, der dann verwendet wird, um die zugehörige Methode nachzuschlagen und aufzurufen.
Sie können die Reflexion durch ein beliebiges anderes System ersetzen. Hashes sind die beste Wahl. Schauen Sie sich jedoch mehrere Versand- und Besuchermuster an, um weitere Ideen zu erhalten.
Was die Eingabe angeht, habe ich mich entschieden, etwas ganz anderes zu tun als das, was Sie denken. Ein Eingabehandler konvertiert die Eingabe von Tastatur / Maus / Gamepad separat in eine Flaggenaufzählung (Bitfeld) möglicher Aktionen (MoveForward, MoveBackwards, StrafeLeft, Use usw.) basierend auf den Benutzereinstellungen / dem, was der Player an den Computer angeschlossen hat. Jede Komponente erhält
UserInputMessage
jedes Bild, das sowohl das Bitfeld als auch die Blickachse des Spielers als 3D-Vektor enthält (Entwicklung der Ausrichtung auf einen Ego-Shooter). Dies macht es auch einfach, Spieler ihre Schlüsselbindungen ändern zu lassen.Wie Sie am Ende Ihrer Frage gesagt haben, gibt es viele verschiedene Möglichkeiten, ein komponentenbasiertes Entitätssystem zu erstellen. Ich habe mein System stark auf das Spiel ausgerichtet, das ich mache, daher sind einige der Dinge, die ich tue, natürlich im Zusammenhang mit einem RTS möglicherweise nicht sinnvoll, aber es ist immer noch etwas, das implementiert wurde und für das gearbeitet wurde ich bisher.
quelle