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\$
  • \$\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\$ – Laurent Couvidou May 28 '14 at 7:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.