Do you understand "Evolve Your Heirarchy"?

Started by
12 comments, last by Antheus 16 years, 10 months ago
There's basically 2 ways to implement this sort of architecture.

1) All components inherit from a base class which gives a basic interface for storing the components. Something like a couple pointers for a linked list node.
Then, your Entity class will contain this linked list of components. This is a very easily-to-grasp method of managing your components. They're just stored in the entity that they comprise.
The downside of doing it this way is that it isn't very easy to "find" a particular component. When you need to send a message to a component, you end up iterating through many entities, even if they don't contain a component of the type that accepts the message. It's still a viable way to keep track of your components. And if your entities are all very similar, this may be the best way to go.

2) The Database approach. (no, not kind of database) This is the way it was done in the Game Programming Gems 5 example. And it's the method I prefer.
Basically, components are stored in a series of datastructures which are focused much more around the Components, not the entities.
I don't have my GPG5 book here, so I can't detail all the datastructures they hold the component pointers in. But they are mostly things like a std::map of std::list, so you can search the map for components of a certain type, and then iterate through the list sending messages to all the components which can receive that message. All components have an "Entity ID" member variable, but that's the extent of thier association with a particular entity. The cool thing about this approach, is that there _is_no_entity_ entities are nothing more than ID numbers stored in various components.

This method seems better to me, but I can see some of the benefits of the entity-centric approach. The good thing is, if I decide that I need to hold on to an entity for some reason, I can always make one, and just add that as a new data-structure in the database object.

I'm sure this has been very confusing, especially the method #2 explaination. If you can get your hands on the GPG5 source-code or book, it does a MUCH better job of explaining the architecture than I ever could.

Advertisement
I believe method 2 described by Nohup is what the article was talking about, and it is more general than the entity-centric approach. The problem with using interfaces and then having your components inherit from the interfaces they support is that you're kind of stuck with blobs. I'd rather have a more data-type oriented interface between components, or dependency injection.

For example, Component A holds a pointer to a class that takes a vector as input, and Component B implements that class (in C++ maybe templates are used to avoid the virtual method call). Component A and Component B are both part of a certain entity, so they are created and A is connected to B.

Maybe B is this 'movable' component, and it takes a vector and adds it to a position component that represents the location of the entity, and A is an component designed to output a series of rotated vectors. Now the entity spins in circles.

Or A could generate vectors based on user input (either keyboard or mouse, maybe a flag to control this or separate components), and now the user controls the entity. Or maybe B is part of a physics system and accumulates a force, or sets acceleration, or selects an adjacent component in a gui.

Now we decide that this entity doubles its speed when a certain game state is active. Throw a component in between that doubles the input vector and then outputs it to the next component when a certain state is in effect. Maybe this component can be parameterized with a factor and a triggering condition, or something more or less general, depending on taste. Maybe the factor can even be controlled in game by attaching another component, or using debug tools, to allow the effect to change or the designer to try different values.

The component system can provide some standard systems for more flexible communication, like broadcast event channels, or these could be implemented as other components.
Ok, so now we've got some different explanations. I'll see if I can get hold of the GP Gems 5 article, maybe it will clear things up a bit.
Quote:Original post by eczkchtl
Ok, so now we've got some different explanations. I'll see if I can get hold of the GP Gems 5 article, maybe it will clear things up a bit.


You really don't. I checked with GPG5 before giving my example.

The difference is in how the interfaces are stored. In my case I used inheritance to determine which interfaces are supported. The GPG5 example uses containers that provide the interfaces.

The Movable (IMove + IMoveHelper in book's example) is perfectly valid (that's incorrect in the above post).

The key to component based design, regardless of how you organize your structures is the following: Interface which defines functionality and a Component manager system which provides connections between references.

When performing actions you're performing them on interfaces, not objects. These interfaces could be implemented by single instance, by separate instances or by some static facilities. But component based design de-couples the notion of functionality being tied into a specific inheritance tree.

As such, anything that implements certain functionality via some interface can serve as perfectly valid building block - opposed to inheritance where in order to implement a player, you might need to inherit from a Creature object, and all that such object has, including AI, spawn mechanisms, etc. Conversly, Creature object inheriting from Player would have keyboard input support, chat system, quest log, scores, etc...

In my example above, the classes Player, DecorativePlant and Missile are *never* used by game systems. You can never request an instance of Player or Missile. They are merely allocated at some point somewhere deep in component manager merely as a convenience for this particular storage.

All other interactions are performed solely on the interfaces used by these classes.

The visitor patter provides the most basic and generic method of processing all these interfaces in sequential fashion. If you extend the rules for interfaces and how they interact with other components, possibly by adding them an ID, or allow them to hold references to other objects, then you do not need to use it anymore.

This topic is closed to new replies.

Advertisement