(Lo que describo se basa en este diseño: ¿Qué es un marco de sistema de entidad? Desplácese hacia abajo y lo encontrará)
Tengo algunos problemas para crear un sistema de entidad-componente en C ++. Tengo mi clase de componente:
class Component { /* ... */ };
Que en realidad es una interfaz para crear otros componentes. Entonces, para crear un componente personalizado, simplemente implemento la interfaz y agrego los datos que se usarán en el juego:
class SampleComponent : public Component { int foo, float bar ... };
Estos componentes se almacenan dentro de una clase Entity, que le da a cada instancia de Entity un ID único:
class Entity {
int ID;
std::unordered_map<string, Component*> components;
string getName();
/* ... */
};
Los componentes se agregan a la entidad haciendo un hash del nombre del componente (probablemente esta no sea una gran idea). Cuando agrego un componente personalizado, se almacena como un Tipo de componente (clase base).
Ahora, por otro lado, tengo una interfaz de sistema, que usa una interfaz de nodo dentro. La clase Node se usa para almacenar algunos de los componentes de una sola entidad (ya que el Sistema no está interesado en usar todos los componentes de la entidad). Cuando el sistema tiene que hacerlo update()
, solo necesita iterar a través de los nodos que almacenó creados a partir de diferentes entidades. Entonces:
/* 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 */
};
Ahora el problema: digamos que construyo los SampleNodes pasando una entidad al SampleSystem. El SampleNode luego "verifica" si la entidad tiene los componentes necesarios para ser utilizados por el SampleSystem. El problema aparece cuando necesito acceder al componente deseado dentro de la Entidad: el componente se almacena en una Component
colección (clase base), por lo que no puedo acceder al componente y copiarlo en el nuevo nodo. He resuelto temporalmente el problema al convertirlo Component
en un tipo derivado, pero quería saber si hay una mejor manera de hacerlo. Entiendo si esto significaría rediseñar lo que ya tengo. Gracias.
fuente
addComponent()
necesitaría el método ser también un método de plantilla? Si defino aaddComponent(Component* c)
, cualquier subcomponente que agregue se almacenará en unComponent
puntero ytypeid
siempre se referirá a laComponent
clase base.Chewy tiene razón, pero si estás usando C ++ 11 tienes algunos tipos nuevos que puedes usar.
En lugar de usarlo
const std::type_info*
como clave en su mapa, puede usarstd::type_index
( consulte cppreference.com ), que es un contenedor alrededor delstd::type_info
. ¿Por qué lo usarías? Enstd::type_index
realidad, almacena la relación con elstd::type_info
como puntero, pero ese es un puntero menos por el que debe preocuparse.Si realmente está usando C ++ 11, recomendaría almacenar las
Component
referencias dentro de punteros inteligentes. Entonces el mapa podría ser algo como:Agregar una nueva entrada podría hacerse así:
donde
component
es de tipostd::shared_ptr<Component>
. La recuperación de una referencia a un tipo determinadoComponent
podría verse así:Tenga en cuenta también el uso de en
static_pointer_cast
lugar destatic_cast
.fuente
cast
. Estoy empezando a pensar que sería imposible implementar esto, o un diseño de sistema similar sin moldes.Component
punteros en un solo contenedor requiere necesariamente convertirlos al tipo derivado. Pero, como señaló Chewy, tiene otras opciones disponibles, que no requieren lanzamiento. Yo mismo no veo nada "malo" en tener este tipo de modelos en el diseño, ya que son relativamente seguros.