views:

122

answers:

3

I've been batting this problem around in my head for a few days now and haven't come to any satisfactory conclusions so I figured I would ask the SO crew for their opinion. For a game that I'm working on I'm using a Component Object Model as described here and here. It's actually going fairly well but my current storage solution is turning out to be limiting (I can only request components by their class name or an arbitrary "family" name). What I would like is the ability to request a given type and iterate through all components of that type or any type derived from it.

In considering this I've first implemented a simple RTTI scheme that stores the base class type through the derived type in that order. This means that the RTTI for, say, a sprite would be: component::renderable::sprite. This allows me to compare types easily to see if type A is derived from type B simply by comparing the all elements of B: i.e. component::renderable::sprite is derived from component::renderable but not component::timer. Simple, effective, and already implemented.

What I want now is a way to store the components in a way that represents that hierarchy. The first thing that comes to mind is a tree using the types as nodes, like so:

       component
       /       \
  timer         renderable
  /              /      \
shotTimer   sprite      particle

At each node I would store a list of all components of that type. That way requesting the "component::renderable" node will give me access to all renderable components regardless of derived type. The rub is that I want to be able to access those components with an iterator, so that I could do something like this:

for_each(renderable.begin(), renderable.end(), renderFunc);

and have that iterate over the entire tree from renderable down. I have this pretty much working using a really ugly map/vector/tree node structure and an custom forward iterator that tracks a node stack of where I've been. All the while implementing, though, I felt that there must be a better, clearer way... I just can't think of one :(

So the question is: Am I over-complicating this needlessly? Is there some obvious simplification I'm missing, or pre-existing structure I should be using? Or is this just inheritly a complex problem and I'm probably doing just fine already?

Thanks for any input you have!

+1  A: 

You should think about how often you need to do the following:

  • traverse the tree
  • add/remove elements from the tree
  • how many objects do you need to keep track of

Which is more frequent will help determine the optimum solution

Perhaps instead of make a complex tree, just have a list of all types and add a pointer to the object for each type it is derived from. Something like this:

map<string,set<componenet *>>  myTypeList

Then for an object that is of type component::renderable::sprite

myTypeList["component"].insert(&object);
myTypeList["renderable"].insert(&object);
myTypeList["sprite"].insert(&object);

By registering each obejct in multiple lists, it then becomes easy to do something to all object of a given type and subtypes

for_each(myTypeList["renderable"].begin(),myTypeList["renderable"].end(),renderFunc);

Note that std::set and my std::map construct may not be the optimum choice, depending on how you will use it.

Or perhaps a hybrid approach storing only the class heirarchy in the tree

map<string, set<string> > myTypeList;
map<string, set<component *> myObjectList;

myTypeList["component"].insert("component");
myTypeList["component"].insert("renderable");
myTypeList["component"].insert("sprite");
myTypeList["renderable"].insert("renderable");
myTypeList["renderable"].insert("sprite");
myTypeList["sprite"].insert("sprite");

// this isn't quite right, but you get the idea
struct doForList {
    UnaryFunction f;
    doForList(UnaryFunction f): func(f) {};
    operator ()(string typename) { 
       for_each(myTypeList[typename].begin();myTypeList[typename].end(), func);
   }
}

for_each(myTypeList["renderable"].begin(),myTypeList["renderable"].end(), doForList(myFunc))
MadCoder
I'll need to traverse more than add/remove, and I plan on several hundred to a couple thousand components at a time, depending on the situation. I've considered the method you said and still haven't ruled it out yet, but am not a big fan of the storage requirements. May be the best option, though.
Toji
Have you looked at boost::graph?
MadCoder
I have actually. Considered it strongly too, but some quick tests on my part revealed some non-trivial performance hits when used the way I would need it. Might just be that I was using it wrong. I'll have to take another look.
Toji
Perhaps a hybrid of your solution and mine would work. I'll add it to my answer
MadCoder
Hm... Now that's intriguing! I don't think I could use it in quite that form, but it gives me some ideas! I'll have to play with it a bit.
Toji
A: 

The answer depends on the order you need them in. You pretty much have a choice of preorder, postorder, and inorder. Thus have obvious analogues in breadth first and depth first search, and in general you'll have trouble beating them.

Now, if you constraint the problem a litle, there are a number of old fashioned algorithms for storing trees of arbitrary data as arrays. We used them a lot in the FORTRAN days. One of them had the key trick being to store the children of A, say A2 and A3, at index(A)*2,index(A)*2+1. The problem is that if your tree is sparse you waste space, and the size of your tree is limited by the array size. But, if I remember this right, you get the elements in breadth-first order by simple DO loop.

Have a look at Knuth Volume 3, there is a TON of that stuff in there.

Charlie Martin
A: 

If you want to see code for an existing implementation, the Game Programming Gems 5 article referenced in the Cowboy Programming page comes with a somewhat stripped down version of the code we used for our component system (I did a fair chunk of the design and implementation of the system described in that article).

I'd need to go back and recheck the code, which I can't do right now, we didn't represent things in a hierarchy in the way you show. Although components lived in a class hierarchy in code, the runtime representation was a flat list. Components just declared a list of interfaces that they implemented. The user could query for interfaces or concrete types.

So, in your example, Sprite and Particle would declare that they implemented the RENDERABLE interface, and if we wanted to do something to all renderables, we'd just loop through the list of active components and check each one. Not terribly efficient on the face of it, but it was fine in practice. The main reason it wasn't an issue was that it actually turns out to not be a very common operation. Things like renderables, for example, added themselves to the render scene at creation, so the global scene manager maintained its own list of renderable objects and never needed to query the component system for them. Similarly with phyics and collision components and that sort of thing.

James Sutherland