So I'm trying to make an more sophisticated component system, most have been not as large, but this time around I'm aiming at highly flexible one so this is my first attempt at making anything decent.
I'v grasped the basic aspects of Components, and the only thing that ever has bothered me about it is communications between all of the components, especially in "larger" ( not 3xA large but larger than Pong or Tetris ) "games".
Now I'v came up with an design that I feel comfortable with ( In some aspects anyways ), It's inspired from Don't Starves Lua-Component system as well as a topic ( it can be found here: http://gameprogrammingpatterns.com/component.html#when-to-use-it ).
Basically the Entity ( Container Object ) have an array/vector/map of components that is indexed by an enum-table called "ComponentID", which is where all the components are defined as integer ids.
This helps to sort each component based on it's "priority" which in turn makes sure they are called in the right order as well as clarifies in code what it refers to.
Entity has under 'protected' two methods called "hasComponent(ComponentID id)" & getComponent(ComponentID id), the later is a template'd function that dynamically casts the right class depending of the ID.
The Component Interface has access to these via friend-keyword seeing as those functions should be exposed but not publicly. Here is the syntax I'm currently targeting within my components, this is from the component "Movable" - which handles movement and coordinates.
/* Inside the Movable.cpp */
if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Down ) )
{
float ny = y + MOVEMENT_SPEED * dt;
}
if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Up ) )
{
float ny = y - MOVEMENT_SPEED * dt;
}
if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Right ) )
{
float nx = x + MOVEMENT_SPEED * dt;
}
if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Left ) )
{
float nx = x - MOVEMENT_SPEED * dt;
}
/* If an entity does have an Physics Component. */
if ( entity.hasComponent( ComponentID::PHYSICS ) )
{
/* Request it as such. */
Physics* p = entity.getComponent<Physics*>( ComponentID::PHYSICS );
/* Check if it's solid and there is no tile/terrain collision */
if ( p->isSolid() && !p->terrainCollision( nx, ny, entity ) )
{
x = nx;
y = ny;
}
}
else /* Just move it as such. */
{
x = nx;
y = ny;
}
I know it's not exactly perfectly decoupled, but this is my first time around and I though it was an easy and flexible way of getting data from other components that said component needs, such as Graphic-Component needs coordinates where to draw the sprite, or in this case, if an Physics-Component exists then check if the object is solid and make sure it doesn't collide.
Now here is where you come in, I'm wondering if this is really a good, or acceptable way of handling communications/interactions between components? Am I doing the component pattern completely wrong or am I on the right track?
Kind regards, Moonkis.
EDIT:
If anyone is wondering the function: "Entity::getComponent(ComponentID id)" will be using static_cast<> because I'v read that it performs WAY better than dynamic_cast<> and I am confident I'm trying to cast an correct type ( the map<ComponentID, IComponent*> looks like that ).