I'm working on a school project developing a game. We use an engine made by one of the teams we have. The build-up of the engine is unclear to me and seems an anti-pattern. However, it seems that no one can make the design choices clear to me. The engine is supposed to use a "Component-based" design but I don't see it. Below is the code of both the Component, Composite and Entity class. My question in short is this: is this code using a valid design pattern or is it overcomplicated just for the sake of 'implementing a design pattern', thereby causing an antipattern?
Component.cpp:
#include "Engine\Component.h"
#include "Engine\Composite.h"
Component::Component(Composite* parent)
{
this->parent = parent;
}
Component::~Component()
{
}
Entity.cpp
#include "Engine\Entity.h"
#include "Engine\Game.h"
Entity::Entity(Composite* parent):Composite(parent)
{
this->mass = 1;
node = NULL;
}
void Entity::update()
{
Composite::update();
this->angularVelocity += this->angularAccelaration;
this->orientation += this->angularVelocity;
this->accelaration = (1 / this->mass) * this->force;
this->velocity += this->accelaration;
this->position += this->velocity;
if (node != NULL)
{
this->node->setPosition(this->position);
this->node->setRotation(this->orientation);
}
}
void Entity::draw()
{
Composite::draw();
if (node == NULL) return;
if (!this->visible)
{
this->node->setVisible(false);
return;
}
this->node->setVisible(true);
this->node->render();
}
void Entity::createNode(std::string modelPath)
{
// Get the mesh
irr::scene::IAnimatedMesh* mesh = Game::getSceneManager()->getMesh(modelPath.c_str());
// Create model entity
this->node = Game::getSceneManager()->addMeshSceneNode( mesh );
this->node->setMaterialFlag(EMF_FOG_ENABLE, true);
}
Entity::~Entity()
{
Composite::~Composite();
if (node != NULL)
{
node->drop();
}
}
Composite.cpp
#include "Engine\Composite.h"
Composite::Composite(Composite* parent):Component(parent)
{
}
Composite::~Composite()
{
for (std::list<Component*>::iterator i = components.begin(); i != components.end(); ++i)
{
delete (*i);
}
components.clear();
}
void Composite::handleMessage(unsigned int message, void* data)
{
for (std::list<Component*>::iterator i = components.begin(); i != components.end(); ++i)
{
(*i)->handleMessage(message, data);
}
}
void Composite::update()
{
for (std::list<Component*>::iterator i = components.begin(); i != components.end(); ++i)
{
(*i)->update();
}
}
void Composite::draw()
{
for (std::list<Component*>::iterator i = components.begin(); i != components.end(); ++i)
{
(*i)->draw();
}
}
void Composite::addComponent(Component* component)
{
components.push_back(component);
}
void Composite::removeComponent(Component* component)
{
components.remove(component);
delete component;
}
And the next piece of code is Player.cpp using both composite and entity as a hybrid type of object (I really don't get the logic).
Player.cpp
#include "Player.h"
#include "Messages.h"
#include <iostream>
Player::Player(Composite* parent) : Entity(parent)
{
createNode("../assets/sydney.md2");
//i = 0;
//v3f = vector3df(0,0,0);
/*networker = new NetworkComponent();
addComponent(networker);
networker->registerVar(&i);
networker->registerVar(&v3f);*/
}
void Player::update() {
Composite::update();
//std::cout<<i<<std::endl;
//std::cout<<"vectorx="<<v3f.X<<"\n";
}
void Player::handleMessage(unsigned int message, void* data) {
switch(message) {
case DAMAGE: /* Do something */;
}
delete data;
}
Player::~Player()
{
Entity::~Entity();
}
I don't believe this is a component-based design at all. Shouldn't Entity be deleted and only Composite and Component be used. Shouldn't the component base class be empty and never used directly? Like a component called 'Rigidbody' holding a data structure for rigidbody data and some functions overriding a fully virtual component base class?
The posted code is a variant of a the composite pattern. This design pattern is structural pattern used to allow clients to treat individual objects and complex objects, such as those composed of multiple objects, in a uniform manner. For instance, a rendering loop can iterate over a collection of objects, calling draw()
on each of them. As this is a structural pattern, it is difficult to subjectively answer if this is instance of overengineering, as it would require examining more class hierarchies and architecture.
However, neither the class naming conventions of Component
and Composite
nor the use of the composite design pattern implies this is a "component-based" design. I was not familiar with the game programming component pattern, but it essentially appears to be the strategy pattern with state coupled within the algorithm class, resulting in a simplified interface between strategy
and context
. In either case, these two patterns are behavioral patterns that accomplish interchangeable and encapsulated algorithms. Therefore, the posted code does not implement a "component-based" design, as neither the Component
, Composite
, Entity
, nor Player
class provides a means to encapsulates the algorithms in an interchangeable manner. For example, Entity::update()
will always calculate the position in the same manner. This coupling requires the Entity
class hierarchy to be expanded if Entity
needs to use a different physics model (consider the case of an Entity
warping to a planet with a different set of physics), rather than delegating to a Physics
hierarchy of classes that encapsulate the algorithms.