Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm working on a small C++ component based game engine, as such...

Object Foo might extend GameObject and x number of component interfaces, like Renderable and/or Physical. So let's assume that Foo looks like this:

class Foo : public GameObject, public Renderable { ... }

...and Renderable looks like this:

class Renderable
{
    virtual void render(void) = 0;
}

So, my engine is divided into sub-systems like the Renderer and AssetManager, etc. and all of those GameObjects are stored in some kind of container, for simplicity sake lets say it's an array.

My question is simple, it's the Renderer's job to hit up all of the game objects that are Renderable and do some work on them, how then can I test or know which objects are Renderable in this component-based approach?

Or furthermore, should I be testing each object in the first place or is there a better way for the Renderer to do it's job (perhaps the Renderable interface should attach each GameObject to a list maintained by the Renderer upon creation)?

Thanks, -Cody

share|improve this question
2  
This is a well-written question, but it is also one that has been discussed a lot before (gamedev.stackexchange.com/questions/13797/… and gamedev.stackexchange.com/questions/31473/… are good places to start). There's a few different ways you could go about it, and many questions on this site cover your options. –  michael.bartnett Jan 9 '13 at 6:16
    
Also, what you're describing isn't really what folks traditionally consider a component system for game entities. Components are generally given to an object via aggregation/composition rather than inheritance. Also, if you're planning to do "mixins" with C++ multiple inheritance, you want to keep it lightweight. GameObject and Renderable sound pretty heavy. –  michael.bartnett Jan 9 '13 at 6:19
add comment

1 Answer

up vote 4 down vote accepted

Components should register themselves with the appropriate system. You could do this in the constructor/destructor, though I'd highly recommend some explicit OnCreate() and OnDestroy() methods or some such.

You'd have something like so:

void Renderable::OnCreate() {
  g_Renderer->RegisterRenderable(this);
}

void Renderable::OnDestroy() {
  g_Renderer->UnregisterRenderable(this);
}

Your Foo class, going with your explicit inheritance-based composition, would then just have:

void Foo::OnCreate() {
  GameObject::OnCreate();
  Renderable::OnCreate();
}

void Foo::OnDestroy() {
  GameObject::OnDestroy();
  Renderable::OnDestroy();
}

That said, I don't particularly like the inheritance-based approach you have. It destroys one of the most important parts in my opinion of the component-based design approach: data-driven design.

Instead of making a bunch of specific hard-coded classes that combine components, you can take an approach where a GameObject is just a container of Component objects. This can range from the simple literal interpretation where GameObject is a class that actually has some kind of container of Component objects to an extreme abstraction where GameObjects are just identifiers that relate Component objects together, or you can do something more reasonable in between.

Going with the simple approach, take something like so:

class IComponent {
public:
  virtual void OnCreate() = 0;
  virtual void OnDestroy() = 0;
};

class Renderable : public IComponent {
public:
  virtual void OnCreate() { g_Renderer->Register(this); }
  virtual void OnDestroy() { g_Renderer->Unregister(this); }
};

class GameObject {
  std::vector<IComponent*> m_Components;

public:
  ~GameObject() {
    foreach (IComponent* cmp : m_Components) {
      cmp->OnCreate();
      delete cmp;
    }
  }

  template <typename Type>
  void AddComponent() {
    m_Components.push_back(new Type());
    m_Components.back()->OnCreate();
  }

  template <typename Type>
  Type* GetComponent() {
    foreach (IComponent* cmp : m_Components) {
      Type* typed_cmp = dynamic_cast<Type*>(cmp);
      if (typed_cmp != nullptr)
        return typed_cmp;
    }
    return nullptr;
  }
};

Obviously that's hardly the most efficient implementation, but it gets the point across.

With an implementation like that, you can read in a data file that describes your objects. A game designer can use your game editor (or a text editor, if you haven't put in the time to make a useful editor) to compose objects as needed, without needing to go bug a developer and wait for a new build just to get some new behavior.

Even if there's not much need for finding new crazy combinations of components (but there is, especially once you allow scripts to define new components, so that you aren't stuck with the usual component-based engine with its "five C++ components and then helter skelter in script management" brilliance), it's at least handy for a designer to be able to add or remove components at runtime. Say that you had created two classes, StaticMeshDecoration and StaticMeshCollidable, the latter of which has a PhysicsBody component. In your editor, it would be really lame if a designer had created a StaticMeshDecoration, decided it needed physics, and then had to delete the object and recreate/reconfigure it all over as a StaticMeshCollidable.

On a final unrelated note, don't have a virtual method render on Renderable. It's not the individual object's job to render things. The whole point of having a Renderer object is that it takes the data of the renderable objects and then intelligently and efficiently figures out how to render the scene best. You should have a handful of specific Renderable component types that individually register themselves in the appropriate data structures in your renderer as meshes with all appropriate metadata (materials, shaders, etc.).

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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