Aby komponenty mogły aktualizować każdą ramkę (i pozostawić tę funkcjonalność poza komponentami, które nie muszą), wpadłem na pomysł, aby utworzyć komponent UpdateComponent. Inne elementy, takie jak MovableComponent
(które utrzymują prędkość), dziedziczyłyby po IUpdatable
klasie abstrakcyjnej. Wymusza MovableComponent
to implementację Update(gametime dt)
metody i innej, RegisterWithUpdater()
która daje UpdateComponent
wskaźnik do MovableComponent
. Wiele komponentów może to zrobić, a następnie UpdateComponent
może wywołać wszystkie swoje Update(gametime dt)
metody bez konieczności dbania o to, kim lub czym są.
Moje pytania to:
- Czy to wydaje się być czymś normalnym lub używanym przez kogokolwiek? Nie mogę nic znaleźć na ten temat.
- Jak mogę utrzymać porządek w elementach takich jak fizyka, a następnie zmienić pozycję? Czy to w ogóle konieczne?
- Jakie są inne sposoby zapewnienia, że komponenty, które powinny być przetwarzane w każdej ramce, są w rzeczywistości przetwarzane?
EDYCJA
Myślę, że będę zastanawiał się, jak dać menadżerowi encji listę typów, które można aktualizować. Wtedy WSZYSTKIE komponenty tego typu mogą się aktualizować zamiast zarządzać nimi dla poszczególnych jednostek (które i tak są tylko indeksami w moim systemie).
Nadal. Moje pytania pozostają dla mnie ważne. Nie wiem, czy jest to uzasadnione / normalne, lub co robią inni.
Również ludzie w Insomniac są niesamowici! /EDYTOWAĆ
Gotowany kod dla poprzedniego przykładu:
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_;
};
źródło
Odpowiedzi:
Jedną z głównych zalet systemu komponentów jest możliwość korzystania z wzorców buforowania - dobry icache i predykcja, ponieważ ciągle uruchamiasz ten sam kod, dobry dcache, ponieważ możesz alokować obiekty w jednorodne pule i ponieważ tabele vtable, jeśli każdy, bądź gorący.
Sposób, w jaki ustrukturyzowałeś swoje komponenty, ta przewaga znika całkowicie i w rzeczywistości może stać się obciążeniem wydajnościowym w porównaniu do systemu opartego na dziedziczeniu, ponieważ wykonujesz o wiele więcej wirtualnych połączeń i więcej obiektów za pomocą vtables.
To, co powinieneś zrobić, to przechowywać pule według typu i iterować każdy typ niezależnie, aby wykonać aktualizacje.
Nie jest tak powszechne w dużych grach, ponieważ nie jest korzystne. Jest powszechny w wielu grach, ale nie jest interesujący technicznie, więc nikt o tym nie pisze.
Kod w językach takich jak C ++ ma naturalny sposób wykonywania poleceń: wpisz instrukcje w tej kolejności.
W rzeczywistości nie ma to sensu, ponieważ żaden solidny system fizyki nie ma takiej struktury - nie można zaktualizować jednego obiektu fizyki. Zamiast tego kod wyglądałby bardziej jak:
źródło
Myślę, że to, o czym mówisz, jest rozsądne i dość powszechne. To może dać ci więcej informacji.
źródło
Lubię te podejścia:
Krótko: Unikaj zachowania aktualizacji w obrębie komponentów. Składniki nie są zachowaniem. Zachowanie (w tym aktualizacje) można wdrożyć w niektórych podsystemach z pojedynczą instancją. Takie podejście może również pomóc w przetwarzaniu wsadowym podobnych zachowań dla wielu składników (być może przy użyciu instrukcji parallel_for lub SIMD na danych składników).
Pomysł IUpdatable i metoda aktualizacji (gametime dt) wydają się nieco zbyt restrykcyjne i wprowadzają dodatkowe zależności. Może być dobrze, jeśli nie używasz podejścia podsystemów, ale jeśli go używasz, IUpdatable to nadmiarowy poziom hierarchii. W końcu MovingSystem powinien wiedzieć, że musi aktualizować komponenty Location i / lub Velocity bezpośrednio dla wszystkich encji, które mają te komponenty, więc nie ma potrzeby stosowania jakiegoś pośredniego komponentu IUpdatable.
Możesz jednak użyć komponentu Aktualizowalnego jako mechanizmu pomijania aktualizacji niektórych jednostek, mimo że mają one komponenty lokalizacji i / lub prędkości. Składnik aktualizowalny może mieć flagę bool, którą można ustawić
false
i która zasygnalizuje każdemu podsystemowi obsługującemu aktualizację, że ten konkretny byt nie powinien być obecnie aktualizowany (chociaż w takim kontekście nazwa Freezable wydaje się bardziej odpowiednią nazwą dla komponentu).źródło