2
\$\begingroup\$

After struggling with various problems and reading up on component-based systems and reading Bob Nystrom's excellent book "Game Programming Patterns" and in particular the chapter on Components

I determined that this is a horrible idea:

//Class intended to be inherited by all objects. Engine uses Objects exclusively.
class Object : public IUpdatable, public IDrawable {
public:
    Object();
    Object(const Object& other);
    Object& operator=(const Object& rhs);

    virtual ~Object() =0;

    virtual void SetBody(const RigidBodyDef& body);
    virtual const RigidBody* GetBody() const;
    virtual RigidBody* GetBody();

    //Inherited from IUpdatable
    virtual void Update(double deltaTime);

    //Inherited from IDrawable
    virtual void Draw(BITMAP* dest);


protected:
private:
};

I'm attempting to refactor it into a more manageable system. Mr. Nystrom uses the constructor to set the individual components; CHANGING these components at run-time is impossible. It's intended to be derived and be used in derivative classes or factory methods where their constructors do not change at run-time. i.e. his Bjorne object is just a call to a factory method with a specific call to the GameObject constructor.

Is this a good idea? Should the object have a default constructor and setters to facilitate run-time changes or no default constructor without setters and instead use a factory method?

Given:

class Object {
public:
    //...See below for constructor implementation concerns.
    Object(const Object& other);
    Object& operator=(const Object& rhs);

    virtual ~Object() =0;

    //See below for Setter concerns
    IUpdatable* GetUpdater();
    IDrawable* GetRenderer();

protected:
    IUpdatable* _updater;
    IDrawable* _renderer;
private:
};

Should the components be read-only and passed in to the constructor via:

class Object {
public:
    //No default constructor.
    Object(IUpdatable* updater, IDrawable* renderer);
//...remainder is same as above...
};

or Should a default constructor be provided and then the components can be set at run-time?

class Object {
public:
    Object();
//...
SetUpdater(IUpdater* updater);
SetRenderer(IDrawable* renderer);
//...remainder is same as above...
};

or both?

class Object {
public:
    Object();
    Object(IUpdater* updater, IDrawable* renderer);
//...
SetUpdater(IUpdater* updater);
SetRenderer(IDrawable* renderer);
//...remainder is same as above...
};
\$\endgroup\$
3
\$\begingroup\$

Ideally, your Object should have no explicit reference to its components: IUpdater, IDrawable, etc. It should rather maintain a list of pointers to a base class, ideally serialized from data:

class Object {
public:
    Object(const char* fromFile); // serializes _componentList
private:
    std::list< IComponent* > _componentList;
};

The rational is simple: the goal of a component-based architecture is to allow for modularity. If you reference everything explicitely you'll soon introduce direct dependencies between your components: removing them, adding new ones of replacing some will become a much more cumbersome task.

\$\endgroup\$
2
  • \$\begingroup\$ How would the system know which components are available? Or worse, which methods? \$\endgroup\$
    – Casey
    May 28 '14 at 2:56
  • \$\begingroup\$ @Casey By browsing the component list, having a hash table of components ordered by name/id, etc. There are many ways of doing this without storing explicit dependencies. And working directly with the components methods is also some kind of explicit referencing: it's OK when you want the dependency to be explicit, but when it's not necessary you should work with messages. \$\endgroup\$ May 28 '14 at 7:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .