Brian - June 7th, 2008 - A Question of Component-Based Design
I've been tooling around with component-based design, as talked about by the Dungeon Siege crew and various posts on gamedev.net. The basic premise is that instead of some gigantic inheritance tree, you have a much flatter tree where objects are defined as a collection of components. As an example, consider a unit in an RTS game. In an inheritance-based scheme, Unit might inherit from Entity which might inherit from Renderable which might inherit from GameObject. The problem with having giant inheritance trees is that code tends to harden and changes become significantly more difficult.
Instead, in a component-based scheme, you have a single object type (we'll call it GameObject) which has very little data - a unique identifier, perhaps, but more importantly, a list of components (we'll call them GameComponent). The GameComponents come in two flavors - they specify data, such as position or velocity, or they specify behaviors, such as what to do when an OnFrame event is called. This has the primary advantage of making your entity system very data-driven. Instead of relying on deeply integrated, programmer-defined structures, you can specify types of GameObjects in an XML schema specifying the object's GameComponents and default values for the data components. No programming required. What's more, you could potentially reload these schemas on the fly, allowing for rapid iteration on your game implementation. What's even more, many of your behaviors could be specified in external script files, allowing for even more dynamic control as the game runs.
I like this system, I think it has a lot of potential, but I haven't worked out all the problems in my head yet. For instance, there's the problem of how other subsystems see relevant components (or even if they should). The World subsystem will probably need to know all the entities that have a RenderableComponent such that when the World goes to draw the screen, it has the components to draw. My solution so far is as follows:
The EntityRegistry (where all GameObjects are placed upon addition to the game) specifies two event types: OnEntityAdded and OnEntityRemoved. The World object registers its own handling functions to these event types, thus listening for addition and removal of entities. Whenever an entity is added, the World object queries whether that entity has an appropriate component, and adds that component to its internal list if so. Upon entity removal, the World object removes the component if necessary.
Of course, the mapping doesn't always go in this direction. For instance, the SoundPlayComponent needs a SoundPlayer to perform its operation. So when an entity is added, the SoundPlayer listens for this, and adds itself to the SoundPlayComponents internal pointer to a SoundPlayer object.
I'm not sure how I feel about the above. It seems to me decently elegant, but it also seems like it couples practically everything to this entity system in some way. Why should a SoundPlayer even know about the SoundPlayerComponent? It doesn't need a SoundPlayerComponent to do itsjob. Now that I think about it, though, I might be able to add a level of indirection - SoundPlayerRegistrar - that listens for the events instead and adds a SoundPlayer to the appropriate components. Thus, the SoundPlayer itself never needs to know about game components. Other solutions are welcome here.
Two other critical problems plague me: communication between components in the same entity, and communication between components of other entities.
Communication between components in the same entity is key. When a motion event is fired in one component, for instance, another component may need to receive that event to handle, say, physics. The problem here is that the order components are added is unpredictable, and there's really no common place for components to see each other. My idea - one that currently makes me feel a little nauseous - is to have all the event types stored in the parent GameObject. Then components interface through that GameObject to register themselves for events or to signal events. It will work, but it feels wrong.
Then there's communication between components of other entities, which I'm not even sure is an issue. It seems like it could be a problem in my head, but it also seems like it could be a non-issue if other subsystems are always handling the interaction between entities. For intsance, it may seem like a CollisionAnalyzerComponent would need to know all about the other entities to find out if there's a collision, but in reality there would be some CollisionAnalyzer subsystem that all the entities with that component would get registered with, and that would handle the interaction. I'm not sure if this is something I'll know the answer to until I run into a problem that needs cross-entity communication among components. Thoughts on this?
My current idea is to prototype a component-based system while making a rough XNA game. Penguin Push might get ported over in preparation for when XBox Live starts accepting user-submitted games, though I wish I had the art assets to drop in. Maybe Szarko would like to redraw the 7 or so sprites that are in the game.
Stop ringing!




June 8th, 2008 at 1:42 pm
Some initial thoughts though:
What if the GameObject base had, in its Before/After frame event, a bit of code that iterated through the Compenent list and did some corresponding action based on all of the potential GameCompenents? For things like Sound and Collision the code in the loop would call some static manager like a SoundPlayer and CollisionAnalyzer passing it information from the GameCompenent (enityId, soundFile, etc.) and more interestingly, it could receive information from these calls (like who it collided with). This however leads to the cross-entity communication issue, could this be handled via the EntityRegister?
Regardless, cool article. You should keep stuff like this coming.
June 10th, 2008 at 8:15 pm
[...] Component Based Game Architectures - GDNet Post Outboard component-based entity system architecture - GDNet post Object Oriented Game Programming: The Behavior System 301: Introduction to Dungeon Siege Architecture Various Bilas presentations Some stuff written by Jeremy Chatalaine The Animal Farm Creations: A Question of Component-Based Design [...]