• 12
• 12
• 9
• 10
• 13

# Sub-system Services, Inheritance or Composion

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

## Recommended Posts

Quote:
 Original post by dmatter That looks better than the original. Although, isn't terrain a type of obstacle? I am a little concerned with the use of inheritance (I presume it's inheritance), particularly with the Player class. In a more complete class diagram would you be having the Player class inherit from Collidable, Moveable, Drawable, Audible, etc? That's a lot of multiple inheritance! I would prefer composition over inheritance, the Player class can compose a Collidable component, a Drawable component, etc.
I'm actually partial to the former, IEntity, ICollidible, IRenderible, IAudible, IPhysical, etc... I use IEntity as the base interface, and entities are added to the game-world. When they are added that are interrogated (dynamic casting) for the service-interfaces they implement and registered with the sub-system that provides that service. How would a composition design work, and are there any advantages?

##### Share on other sites
Component-Based Design:
Pros:
* New entity types can be created on the fly (at runtime)
* Open for extension, component code can be reused to make totally different entities. If you want a new behavior, create a new component and subsystem pair.
* separates data from implementation (implementation in the subsystems, data in the components)
* modular
Cons:
* Messaging and events can be harder to implement, and potentially slower

Inheritance-Based Design:
Pros:
* More similar to how humans think/the way the world is composed
* Faster in general
* Messaging and events can be easier to implement and faster
* Good information/implementation hiding
Cons:
* Can be abused: god classes, etc.
* Poor code reuse
* Entity types/functionality fixed at compile time, not configurable

I'm a definite proponent of the component based design. It's better suited to general purpose game engines. However, inheritance-based designs are perfectly valid for simple, single-purpose games. Basically, if you're making an engine, use a component system, and if you're making a simple game, use specialized inheritance. If you're making a more complicated game, consider using a component-based design because it is more flexible in the long run, but inheritance is still a valid choice, despite that I don't recommend it.

##### Share on other sites
Let's say the 'physics' or 'collidable' component detects the object has been hit and this needs to trigger a knock-down' animation and a 'been-smacked' audio clip. You're isolated in the collidable component, does it have to send a message to its parent to tell it to change state? Seems like each of these components would have to have a back-pointer to the object that owns them (or pump more messages?)

If you're implementing interfaces, the object that gets hit is told it's hit and has direct access to all its interrelated sub-components. You can create a 'stock' implementation that suffices for most boids and then special ones that act differently (e.g. the player, maybe a special boss, terrain, etc...).
I see how the component system would allow for mix & matching at run-time, but when would use it at run-time?

##### Share on other sites
I forgot to mention that the glue holding all of this together for the component system is a scripting engine.

The scripting engine has bindings for each class and it's methods. One creates an entity in script and therefore has access to all of it's components as well through that script. The logic for an entity is separate from it's class/components. For instance, every frame, the script could poll it's Entity's Collision Component to see if it collided with anything. It would then get the data for the collision from that component (point/normal,etc.) and then choose what to do. It could play a sound, an animation or whatever.

##### Share on other sites
Heh, reading a quote of myself was the last thing I expected to be doing when I clicked to view this thread [grin]

Quote:
 Original post by Shannon BarberLet's say the 'physics' or 'collidable' component detects the object has been hit and this needs to trigger a knock-down' animation and a 'been-smacked' audio clip.

To explain this, it's advantageous to compare the scenario against the Multiple-Inheritance (lets abbreviate to 'MI') based model.
For demonstrative purposes I'll take a relatively simplistic approach to implement both models.

You might achieve the aforementioned task with the MI-model like so:
class MyCoolPlayer : public IEntity, public ICollidable, public IAnimatable, public IAudible // ...etc...{public:    // This method is overriden from IEntity    void onUpdate()    {        if ( this->checkCollisions() )        {            this->beginAnimation( KNOCK_DOWN );            this->beginSoundEffect( BEEN_SMACKED );        }    }};

Ok.
Now, since the inheritance hierarchy is determined statically, I will opt for a statically built component model as it's easier to demonstrate the example with; although a much more dynamic implementation is feasible.

An equally-footed component-model approach for this same task might look like this:
class MyCoolPlayer : public IEntity{public:    // This method is overriden from IEntity    void onUpdate()    {        if ( collisionComponent.checkCollision() )        {            animationComponent.beginAnimation( KNOCK_DOWN );            audioComponent.beginSoundEffect( BEEN_SMACKED );        }    }private:    CollidableComponent collisionComponent;    AudibleComponent audioComponent;    AnimatableComponent animationComponent;    // ...etc...};

Obviously both snippets are overly simplified but it makes sense that as you incrementally add sophistication to one model you can similarly add to other also, within reason.
With the MI-model you can't easily build entities at run-time for a truly data-driven and flexible engine, the MI-model can also suffer from all the evil problems associated with multiple-inheritance in general.

Of-course the more data-driven you make things with the component model then the more processing overhead you enevitably have for the heavier use of the event system and/or scripting engine. That said, you could easily still stick with statically bound composition, as demonstrated above, without losing any performance; you might even gain performance since you're not relying so heavily on virtual functions. Either way it's done without messing with multiple-inheritance or deep inheritance hierarchies and the problems they carry with them.

Quote:
 You're isolated in the collidable component, does it have to send a message to its parent to tell it to change state? Seems like each of these components would have to have a back-pointer to the object that owns them (or pump more messages?)

It depends how you implement things; as I demonstrated the components don't need to have back-pointers at all.
This is however the hinge to an interestingly different facet of component based designs: Suppose you say that rather than have entities store a heterogeneous list of many components - you instead have many homogeneous component lists with each component storing a reference to an entity. What you end up with is the basic principle behind Sneftel's out-board component architecture.

Quote:
 I use IEntity as the base interface, and entities are added to the game-world.When they are added that are interrogated (dynamic casting) for the service-interfaces they implement and registered with the sub-system that provides that service.

With the component model you can still have these dedicated service sub-systems. With the conventional composition approach it's now insanely natural to be able to interrogate/query an entity for the the services they compose and it can be done without resorting to blind dynamic casting or touching any RTTI.
The outboard component architecture of-course makes intrinsic use of these service sub-systems anyway.

##### Share on other sites
You don't need to poll when you have a feedback interface to tell you when the event occurred:

class MyCoolPlayer : public IEntity, public ICollidable, public IAnimatable, public IAudible // ...etc...
{
public:
// This method is overridden from ICollidable
void OnCollision(CollisionEvent& collides)
{
this->StartAnimation( KNOCK_DOWN );
sfx->SoundEffect( BEEN_SMACKED );
}
};

The interface-based approached is not what is described as the "traditional-deep-hierarchy" - it's not multiple-inheritance, it's multiple-interface-inheritance.

From what I see, the primary difference in the two approaches is that with a
game component approach the entities own the components, whereas with the interface based approach the sub-systems own the components.

In order to poll the collisionComponent, the collisionComponent must be notified (by the physics/collision sub-system) that collisions have occurred and it must squirrel away the results of those collisions.

Looking over Sneftel's post and linked articles, I think the "components" make a lot of sense as decorators of the object's behavior but not for the engine services they consume.

Effectively, with components the interfaces are on the sub-systems and you write components to talk to them. That seems backwards. The behavior of the sub-systems is well known (they arguably don't need interfaces at all), it's the boids that are deviant (i.e. need interfaces).

i.e. It doesn't make sense to add support for a new type of spatial-sorting at run-time... it does make sense to add a new way to calculate my object's bounding volume.