• Advertisement
Sign in to follow this  

Aggregation/component based system?

This topic is 4736 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

On his site, Kyle Wilson wrote an article talking about aggregation vs. inheritance, which includes this snippet:
Quote:
So if an inheritance-based game object design is riddled with problems, what is a better approach? In redesigning the Plasma engine used by HeadSpin/Cyan, the software engineering team opted to flatten the game object hierarchy down to a single game object class. This class aggregated some number of components which supported functionality previously supported by derived game objects.
Now, I'm not familiar with this design pattern at all. I'd really appreciate a basic explanation of what's going on.

Share this post


Link to post
Share on other sites
Advertisement
Is aggregation the same as implementation? Because if it is, I may be able to help.

Share this post


Link to post
Share on other sites
is that Aggregation vs inheritance?

then it's




// inheritance
class CObject: public CPhysicsObject, public CRenderObject, public CNetworkObject
{

};

// aggregation
class CObject
{
CPhysicsObject* m_pxPhysicsObject;
CRenderObject* m_pxRenderObject;
CNetworkObject* m_pxNetworkObject;
};



Personnaly, I'd prefer aggregation over inheritance. It's the dilemma 'is-a' (inheritance) with 'has-a' (aggregation), to put it simply.

Share this post


Link to post
Share on other sites
zahlman beat me to it, with a hammer.

obviously, you can see the major benefits of composition straight away against inheritance, and the benefits of inheritance (multiple inheritance might be OTT thought) other composition (aka aggregation).

Share this post


Link to post
Share on other sites
I think the article in question might be talking about a system more like plug-ins, where separate components are aggregated/composed into a functional whole.
Quote:
Component-based game objects are cleaner and more powerful than game objects based on inheritance. Components allow for better encapsulation of functionality. And components are inherently dynamic -- they give you the power to change at runtime state which could only be changed at compile time under an inheritance-based design.

This is quite different from "regular" aggregation. I've implemented a system that builds game objects dynamically at runtime from components, and it sounds very similar to what the article is talking about.

Basically, you create a Component interface class which allows you to create and manage trees of components that work together, usually communicating via some event system. It's really nice to be able to change behavior simply by specifying a different component for a game object--even during the game. For example, if I need a game object to play sounds, I simply add a SoundComponent to the object.

There is some overhead since communications must be through events, as you don't normally want to keep naked pointers to components. This decentralization adds complexity, but it's worth it IMHO, because you are now liberated to create objects by mixing and matching parts at runtime rather than relying on a static inheritance hierarchy.

Share this post


Link to post
Share on other sites
hmm... Sounds also like a lot of code to add another component in the game. Since components never inherit from each other (that's the whole point, isn't it?). It's kind of going back to C, with message passing and parsing, and all that jive. If I understand correctly. However, sounds very cool for scripting and general flexibility. The overhead could be quite costly if you have lots of different components in hierarchies. That's my first impressions.

Share this post


Link to post
Share on other sites
It's definitely a much more "flat" class hierarchy, but the flexibility is tremendous, and it's still very OO. So it's not like C in that respect.

For example, you could build a player object from several components which are "composed/aggregated" at runtime (I use XML to define objects):

- KeyInputComponent (react to user input)
- RenderComponent (sprite, textual, point, mesh, etc)
- PhysicsComponent (position, orientation, motion)
- CollisionComponent (method of detection as well as response can differ)
- SoundComponent

These would be contained by the game object (itself a component) which would receive events from the system, passing events to each child component for handling. So if you get a "damage" event, the render component might spawn a particle effect, the physics component might push the object in the direction of damage, the sound component might play an appropriate damage sound.

The coolest thing is that you can change behaviors at runtime, so that if you get a power-up, say, you add a component for it and remove the component when the power-up is gone. In my "2 player pong" game, the balls change state this way, and it works well.

The overhead is in the event system and in the communications between components because they have to "look up" each other to talk. You can't simply say "set the position of the player to x, y." You have to first ask for the PhysicsComponent, then tell that component to set the position. Optimizing that part of the system is what I'm working on for the next iteration of my engine, but I've found the overhead to be pretty slight and well worth the freedom it gives you.

Share this post


Link to post
Share on other sites
Quote:
Original post by griminventions
I think the article in question might be talking about a system more like plug-ins, where separate components are aggregated/composed into a functional whole.
Not really, at least not specifically. oliii's example nails it perfectly.

Now, nothing is stopping you from making CPhysicsObject/CRenderObject/CNetworkObject inherit from a CComponentObject class, and allowing for all sorts of interesting run-time behavior, but it's not strictly necessary.

Personally, I agree with the philosophy. I prefer only to inherit, when I actually plan to use a class through that interface. Even then, I'll arrange my code to avoid having to use classes through multiple interfaces (ie. instead of separate collections of Physics/Render/Network objects, simply add Visitor-style members to the CObject class).

If I had my way, inheritance wouldn't even be linked to polymorphism. We'd all just define interfaces:

class CObject : CFooObject, CBarObject {
...
}

// Matching names/signatures automatically match
interface CObject to CFooObject {
// Otherwise, matches can be specified w/ conversions
someCFoomember(int x, int y, int z) = someCObjmember(Vector3(x, y, z));

}

...

CObject o;
CFooObject* f;
f = &o;
f->someCFooMember(1, 1, 1);



Share this post


Link to post
Share on other sites
I admit my knowledge is not very wide when it comes to these kind of subject. I learn things everyday.

Anyway, when I design classes, I try to answer to both question: is Thing a AnotherThing? (this question gives me interesting bits about the identity of a class). The second question is more complex: do I think that Thing is AnotherThing because Thing extends AnotherThing or because Thing uses AnotherThing? In the later case, of course, I tend to prefer aggregation over inheritance.

Some concrete - and, admittedly, not so trivial - example: consider a generic graphic device interface. Now, consider the OpenGL specialization of such device: is it an extended device of does the generic device uses the OpenGL one? I know, this kind of question looks really weird :)

Regards,

Share this post


Link to post
Share on other sites
Quote:
Original post by griminventions
It's definitely a much more "flat" class hierarchy, but the flexibility is tremendous, and it's still very OO. So it's not like C in that respect.

For example, you could build a player object from several components which are "composed/aggregated" at runtime (I use XML to define objects):

- KeyInputComponent (react to user input)
- RenderComponent (sprite, textual, point, mesh, etc)
- PhysicsComponent (position, orientation, motion)
- CollisionComponent (method of detection as well as response can differ)
- SoundComponent

These would be contained by the game object (itself a component) which would receive events from the system, passing events to each child component for handling. So if you get a "damage" event, the render component might spawn a particle effect, the physics component might push the object in the direction of damage, the sound component might play an appropriate damage sound.

The coolest thing is that you can change behaviors at runtime, so that if you get a power-up, say, you add a component for it and remove the component when the power-up is gone. In my "2 player pong" game, the balls change state this way, and it works well.

The overhead is in the event system and in the communications between components because they have to "look up" each other to talk. You can't simply say "set the position of the player to x, y." You have to first ask for the PhysicsComponent, then tell that component to set the position. Optimizing that part of the system is what I'm working on for the next iteration of my engine, but I've found the overhead to be pretty slight and well worth the freedom it gives you.


if you dont mind, could you please provide an example on what your talking about? i plan on implementing a system you describe in my MORPG, so that the server and client can share code (client only uses certain pieces, server uses others, and i could mix and match as needed). im planning on doing a major overhaul of my code soon and hplus has helped me a lot but any more examples are appreciated.

Share this post


Link to post
Share on other sites
For the curious, here's a sample of how I define a player object in my current game.

<?xml version="1.0"?>

<object name="Jack" type="player" physics="yes" generate_id="no">

<!-- PHYSICS -->
<component type="PhysicsPlayerComponent">
<subscribe name="PhysicsMaster" />
</component>

<!-- INPUT -->
<component type="KeyInputComponent">
<subscribe name="InputMaster" />
<subscribe name="PhysicsPlayerComponent" />
</component>

<component type="SoundComponent">
<sound name="score_points" file="dualists/audio/score_points.wav" volume="0.5" />
</component>

<component type="PlayerInfoComponent">
<subscribe name="RenderMaster" />
<subscribe name="KeyInputComponent" />
<publish name="ObjectMaster" />
</component>

<physical acceleration="2.0" friction="1.0" speed_max="6.0" />

<bounding_box x="10" y="0" width="28" height="128" />

<!-- COLLISION -->
<component type="CollisionNullComponent">
</component>

<component type="CollisionBoundsComponent">
</component>

<collision>
<belongs_to group="player" />
<collide_with group="ball" />
</collision>

<component type="SpriteRenderComponent">
<subscribe name="RenderMaster" />
<subscribe name="PhysicsPlayerComponent" />
<sprite
file="characters/Jack/xml/jack_sprite_l.xml"
animation="idle"
angle_align="no" />
<action name="none" animation="idle" />
<action name="up" animation="move_up" />
<action name="down" animation="move_down" />
</component>

</object>


So the code creates the components from the XML at load time and adds them to a single "game object" parent. It works for what I'm doing with it, and I'm designing the next evolution of it for my next game. If I want to change an object or give it new behaviors, I just specify the necessary components in the XML file and it's done. I don't recompile anything.

I prefer this over static inheritance trees, plus it decouples the code much more. If there's a problem with a component, or a need for change, only that component is changed without affecting any other part of the system.

Adding new components is simple. I only touch 3 source files: the component factory class, the XML parsing class, and the component code itself. Then the component is available to all the game objects.

Share this post


Link to post
Share on other sites
The pseudocode is whatever you want it to be. There's no trickery here; simply build up a complex object out of various other objects. Generally the key is that each part can be swapped at any time for another part which has the same interface (ie. parent class). eg. You might have a parent AIHandler which defines abstract functions such as OnAttack (called when the character is attacked), OnIdle (called every few seconds when he's doing nothing), and so on. Your characters then store a pointer to AIHandler, which could be a PacifistAI (which implements OnAttack by running away), GuardAI (which implements OnIdle by looking around and OnAttack by retaliating), UnconsciousAI (which implements OnIdle with a check to see if it's time to wake up), etc. Just partition behaviour into subclasses and swap between them at will.

Share this post


Link to post
Share on other sites
It's not anything too advanced, but it isn't trivial to build. Here's part of my component interface class, for example.

class IComponent
{
public:
//! Identity
virtual const COMPONENT_ID getId() = 0;
virtual const string& getName() = 0;

//! Process
virtual void update() = 0;

//! Hierarchy
virtual void addChild(IComponent *who) = 0;
virtual void addChild(const COMPONENT_ID whoId) = 0;
virtual void addChild(const string& whoName) = 0;

virtual void removeChild(const COMPONENT_ID whoId) = 0;
virtual void removeChild(const string& whoName) = 0;

virtual IComponent *getChild(const COMPONENT_ID whoId) = 0;
virtual IComponent *getChild(const string& whoName) = 0;
};



This is basically the Composite pattern.

When you call update(), it also calls update() on its children. Child components can be tracked with pointers in a list, for example. I try not to store naked pointers, so I use ids so it can fail safely if the component doesn't exist anymore.

The other half of this equation is the event system, which is another can of worms all its own. :) Take a look at the Observer pattern for some ideas to get you started.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement