Damit Komponenten in der Lage sind, jeden Frame zu aktualisieren (und diese Funktionalität aus Komponenten herauszulassen, die nicht benötigt werden), kam mir die Idee, eine UpdateComponent-Komponente zu erstellen. Andere Komponenten wie MovableComponent
(die Geschwindigkeit halten) würden von der IUpdatable
abstrakten Klasse erben . Dies erzwingt MovableComponent
die Implementierung einer Update(gametime dt)
Methode und einer anderen RegisterWithUpdater()
, die UpdateComponent
einen Zeiger auf die gibt MovableComponent
. Viele Komponenten könnten dies tun und dann UpdateComponent
alle ihre Update(gametime dt)
Methoden aufrufen , ohne sich darum kümmern zu müssen, wer oder was sie sind.
Meine Fragen sind:
- Scheint dies etwas zu sein, das normal ist oder von irgendjemandem benutzt wird? Ich kann nichts zu diesem Thema finden.
- Wie könnte ich eine Reihenfolge für die Komponenten wie Physik aufrechterhalten und dann die Position ändern? Ist das überhaupt nötig?
- Was sind andere Möglichkeiten, um sicherzustellen, dass Komponenten, die in jedem Frame verarbeitet werden sollen, tatsächlich verarbeitet werden?
BEARBEITEN
Ich denke, ich werde überlegen, wie ich dem Entitätsmanager eine Liste der Typen geben kann, die aktualisiert werden können. Dann können ALLE Komponenten dieses Typs aktualisiert werden, anstatt sie pro Entität zu verwalten (die ohnehin nur Indizes in meinem System sind).
Immer noch. Meine Fragen bleiben für mich gültig. Ich weiß nicht, ob dies vernünftig / normal ist oder was andere dazu neigen.
Auch die Leute bei Insomniac sind großartig! /BEARBEITEN
Eingekochter Code für das vorherige Beispiel:
class IUpdatable
{
public:
virtual void Update(float dt) = 0;
protected:
virtual void RegisterAsUpdatable() = 0;
};
class Component
{
...
};
class MovableComponent: public Component, public IUpdatable
{
public:
...
virtual void Update(float dt);
private:
...
virtual void RegisterWithUpdater();
};
class UpdateComponent: public Component
{
public:
...
void UpdateAll();
void RegisterUpdatable(Component* component);
void RemoveUpdatable(Component* component);
private:
...
std::set<Component*> updatables_;
};
quelle
Antworten:
Einer der Hauptvorteile eines Komponentensystems ist die Möglichkeit, Caching-Muster zu nutzen - guter Icache und Vorhersage, weil Sie immer wieder denselben Code ausführen, guter Dcache, weil Sie die Objekte in homogenen Pools zuordnen können und weil die vtables, wenn jeden, bleib heiß.
Durch die Art und Weise, wie Sie Ihre Komponenten strukturiert haben, verschwindet dieser Vorteil vollständig und kann im Vergleich zu einem vererbungsbasierten System zu einer Leistungsbeeinträchtigung werden, da Sie mit vtables weitaus mehr virtuelle Anrufe und mehr Objekte tätigen.
Sie sollten Pools pro Typ speichern und jeden Typ unabhängig iterieren, um Aktualisierungen durchzuführen.
Es ist nicht so häufig in großen Spielen, weil es nicht vorteilhaft ist. Es ist in vielen Spielen üblich, aber technisch nicht interessant, deshalb schreibt niemand darüber.
Code in Sprachen wie C ++ hat eine natürliche Möglichkeit, die Ausführung zu ordnen: Geben Sie die Anweisungen in dieser Reihenfolge ein.
In Wirklichkeit macht das keinen Sinn, da kein robustes Physiksystem so aufgebaut ist - Sie können nicht ein einziges Physikobjekt aktualisieren. Stattdessen würde der Code eher so aussehen:
quelle
Was Sie sprechen, ist vernünftig und ziemlich häufig, denke ich. Dies könnte Ihnen weitere Informationen geben.
quelle
Ich mag diese Ansätze:
Kurz: Vermeiden Sie es, das Aktualisierungsverhalten innerhalb der Komponenten beizubehalten. Komponenten sind kein Verhalten. Das Verhalten (einschließlich Aktualisierungen) kann in einigen Subsystemen mit einer Instanz implementiert werden. Dieser Ansatz kann auch dazu beitragen, ähnliche Verhaltensweisen für mehrere Komponenten stapelweise zu verarbeiten (möglicherweise mithilfe von parallel_for- oder SIMD-Anweisungen für Komponentendaten).
Die IUpdatable-Idee und Update-Methode (gametime dt) scheint etwas zu restriktiv zu sein und führt zusätzliche Abhängigkeiten ein. Es kann in Ordnung sein, wenn Sie keinen Subsystemansatz verwenden, aber wenn Sie ihn verwenden, ist IUpdatable eine redundante Hierarchieebene. Schließlich sollte das MovingSystem wissen, dass es die Location- und / oder Velocity-Komponenten für alle Entitäten mit diesen Komponenten direkt aktualisieren muss, sodass keine IUpdatable-Zwischenkomponente erforderlich ist.
Sie können die aktualisierbare Komponente jedoch als Mechanismus verwenden, um die Aktualisierung einiger Entitäten zu überspringen, obwohl diese über Standort- und / oder Geschwindigkeitskomponenten verfügen. Die aktualisierbare Komponente könnte ein Bool-Flag haben, das gesetzt werden kann
false
und das jedem aktualisierbaren Subsystem signalisiert, dass diese bestimmte Entität derzeit nicht aktualisiert werden sollte (obwohl in diesem Kontext Freezable ein geeigneterer Name für die Komponente zu sein scheint).quelle