What common interface would be appropriate for these game object classes?

Posted by Jefffrey on Game Development See other posts from Game Development or by Jefffrey
Published on 2013-10-22T15:36:25Z Indexed on 2013/10/22 22:04 UTC
Read the original article Hit count: 233

Filed under:
|

Question

A component based system's goal is to solve the problems that derives from inheritance: for example the fact that some parts of the code (that are called components) are reused by very different classes that, hypothetically, would lie in a very different branch of the inheritance tree.

That's a very nice concept, but I've found out that CBS is often hard to accomplish without using ugly hacks. Implementations of this system are often far from clean. But I don't want to discuss this any further.

My question is: how can I solve the same problems a CBS try to solve with a very clean interface? (possibly with examples, there are a lot of abstract talks about the "perfect" design already).

Context

Here's an example I was going for before realizing I was just reinventing inheritance again:

class Human {
public:
    Position position;
    Movement movement;
    Sprite   sprite;
    // other human specific components
};

class Zombie {
    Position position;
    Movement movement;
    Sprite   sprite;
    // other zombie specific components
};

After writing that I realized I needed an interface, otherwise I would have needed N containers for N different types of objects (or to use boost::variant to gather them all together). So I've thought of polymorphism (move what systems do in a CBS design into class specific functions):

class Entity {
public:
    virtual void on_event(Event) {} // not pure virtual on purpose
    virtual void on_update(World) {}
    virtual void on_draw(Window) {}
};

class Human : public Entity {
private:
    Position position;
    Movement movement;
    Sprite   sprite;
public:
    virtual void on_event(Event) { ... }
    virtual void on_update(World) { ... }
    virtual void on_draw(Window) { ... }
};

class Zombie : public Entity {
private:
    Position position;
    Movement movement;
    Sprite   sprite;
public:
    virtual void on_event(Event) { ... }
    virtual void on_update(World) { ... }
    virtual void on_draw(Window) { ... }
};

Which was nice, except for the fact that now the outside world would not even be able to know where a Human is positioned (it does not have access to its position member). That would be useful to track the player position for collision detection or if on_update the Zombie would want to track down its nearest human to move towards him.

So I added const Position& get_position() const; to both the Zombie and Human classes. And then I realized that both functionality were shared, so it should have gone to the common base class: Entity. Do you notice anything? Yes, with that methodology I would have a god Entity class full of common functionality (which is the thing I was trying to avoid in the first place).

Meaning of "hacks" in the implementation I'm referring to

I'm talking about the implementations that defines Entities as simple IDs to which components are dynamically attached. Their implementation can vary from C-stylish:

int last_id;
Position* positions[MAX_ENTITIES];
Movement* movements[MAX_ENTITIES];

Where positions[i], movements[i], component[i], ... make up the entity. Or to more C++-style:

int last_id;
std::map<int, Position> positions;
std::map<int, Movement> movements;

From which systems can detect if an entity/id can have attached components.

© Game Development or respective owner

Related posts about c++

Related posts about architecture