(To, co opisuję, opiera się na tym projekcie: Co to jest struktura systemu encji? Przewiń w dół, a znajdziesz ją)
Mam problemy z tworzeniem systemu encji-komponentów w C ++. Mam swoją klasę Component:
class Component { /* ... */ };
Który jest w rzeczywistości interfejsem do tworzenia innych komponentów. Aby stworzyć niestandardowy komponent, po prostu implementuję interfejs i dodam dane, które będą używane w grze:
class SampleComponent : public Component { int foo, float bar ... };
Te komponenty są przechowywane w klasie Entity, co nadaje każdej instancji Entity unikalny identyfikator:
class Entity {
int ID;
std::unordered_map<string, Component*> components;
string getName();
/* ... */
};
Komponenty są dodawane do encji, mieszając nazwę komponentu (prawdopodobnie nie jest to świetny pomysł). Kiedy dodam niestandardowy komponent, jest on zapisywany jako typ komponentu (klasa bazowa).
Z drugiej strony mam interfejs systemu, który korzysta z interfejsu węzła. Klasa Node służy do przechowywania niektórych komponentów pojedynczej encji (ponieważ System nie jest zainteresowany wykorzystaniem wszystkich komponentów encji). Gdy system musi update()
, musi tylko iterować przechowywane w nim węzły utworzone z różnych podmiotów. Więc:
/* System and Node implementations: (not the interfaces!) */
class SampleSystem : public System {
std::list<SampleNode> nodes; //uses SampleNode, not Node
void update();
/* ... */
};
class SampleNode : public Node {
/* Here I define which components SampleNode (and SampleSystem) "needs" */
SampleComponent* sc;
PhysicsComponent* pc;
/* ... more components could go here */
};
Teraz problem: powiedzmy, że buduję SampleNodes, przekazując encję do SampleSystem. SampleNode następnie „sprawdza”, czy jednostka ma wymagane komponenty do użycia przez SampleSystem. Problem pojawia się, gdy potrzebuję dostępu do żądanego komponentu wewnątrz Entity: komponent jest przechowywany w Component
kolekcji (klasy podstawowej), więc nie mogę uzyskać dostępu do komponentu i skopiować go do nowego węzła. Tymczasowo rozwiązałem problem, przypisując Component
typ pochodny, ale chciałem wiedzieć, czy istnieje lepszy sposób na zrobienie tego. Rozumiem, czy oznaczałoby to przeprojektowanie tego, co już mam. Dzięki.
źródło
addComponent()
metoda nie musi być również metodą szablonową? Jeśli zdefiniuję aaddComponent(Component* c)
, dowolny dodany podskładnik będzie przechowywany weComponent
wskaźniku itypeid
zawsze będzie odnosił się doComponent
klasy podstawowej.Chewy ma rację, ale jeśli używasz C ++ 11, masz kilka nowych typów, których możesz użyć.
Zamiast używać
const std::type_info*
jako klucza na mapie, możesz użyćstd::type_index
( patrz cppreference.com ), który jest opakowaniem wokółstd::type_info
. Dlaczego miałbyś to wykorzystać?std::type_index
Faktycznie przechowuje relacji zstd::type_info
jako wskaźnik, ale to jeden wskaźnik mniej dla ciebie martwić.Jeśli rzeczywiście używasz C ++ 11, polecam przechowywanie
Component
referencji wewnątrz inteligentnych wskaźników. Mapa może więc wyglądać następująco:Można dodać nowy wpis, aby:
gdzie
component
jest typustd::shared_ptr<Component>
. Pobieranie odniesienia do danego typuComponent
może wyglądać następująco:Zwróć też uwagę na użycie
static_pointer_cast
zamiaststatic_cast
.źródło
cast
. Zaczynam myśleć, że wdrożenie tego lub podobnego projektu systemu byłoby niemożliwe bez rzutów.Component
wskaźników w jednym pojemniku niekoniecznie wymaga rzutowania ich na typ pochodny. Ale, jak zauważył Chewy, masz do dyspozycji inne opcje, które nie wymagają rzutowania. Sam nie widzę nic „złego” w tego rodzaju rzutach w projekcie, ponieważ są one względnie bezpieczne.