Jump to content
  • Advertisement
Sign in to follow this  
staticVoid2

Composition vs Virtual Inheritance

This topic is 2777 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

I was wondering what are the benefits of using a component-based entity system over virtual inheritance in C++?

for e.g. In a component system you resolve the diamond inheritance problem by querying interfaces of an entity at runtime like so:


struct Entity {
struct Component {
};
std::vector<Component*> components;
};

struct RenderableComponent : Entity::Component {
void Draw() {
TransformComponent* t = entity->getComponent<TransformComponent>();
t->setTransform();
// draw component
}
}

struct CollidableComponent : Entity::Component {
void TestLine(Line line) {
TransformComponent* t = entity->getComponent<TransformComponent>();
// inverse transform line
// test collision
}
};




In this example you have both a renderable and collidable component which both have a dependancy on a TransformComponent.


But isnt this the exact same as doing:


struct CollidableComponent : public virtual TransformComponent
{
...
};
struct RenderableComponent : public virtual TransformComponent
{
};

struct MyEntity : public CollidableComponent, RenderableComponent
{
};




Except that you are enforcing the dependency at compile-time.
So why is it that using dynamic components to describe an entity better than using virtual inheritance?

Share this post


Link to post
Share on other sites
Advertisement
Virtual inheritance has a number of implementation quirks and stuff you need to be careful of when using it. It's super annoying to provide all of the implementation/override details.

Though it's hard to describe the scope of that. Try them both and judge for yourself.

Share this post


Link to post
Share on other sites
[color="#1C2837"]
It's super annoying to provide all of the implementation/override details [/quote]

[color="#1c2837"]That's one thing I was wondering about, Do components have (or are they supposed to have) any common functionality? Obviously things like update/tick etc. would be essential but why not just define these in the entity class for e.g.

[color="#1c2837"]
struct MyEntity : Entity, RenderableComponent, CollidableComponent {
/* declared in the base entity class */
void Update() {
RenderableComponent::Update();
CollisionComponent::Update();
}
};


[color="#1c2837"]That way you avoid having any virtual functions whatsoever in your component classes.

Share this post


Link to post
Share on other sites

But isnt this the exact same as doing:


struct CollidableComponent : public virtual TransformComponent
{
...
};
struct RenderableComponent : public virtual TransformComponent
{
};

struct MyEntity : public CollidableComponent, RenderableComponent
{
};

It's not the same thing. Among other things, the inheritance-based approach says that a collider 'is' a transform and a renderable component 'is' a transform, while the component-based method simply says that colliders, renderables, and transforms are distinct entities that have some knowledge of and interaction with one another.

[color="#1C2837"]That's one thing I was wondering about, Do components have (or are they supposed to have) any common functionality? Obviously things like update/tick etc. would be essential but why not just define these in the entity class for e.g.

[color="#1c2837"]
struct MyEntity : Entity, RenderableComponent, CollidableComponent {
/* declared in the base entity class */
void Update() {
RenderableComponent::Update();
CollisionComponent::Update();
}
};


[color="#1c2837"]That way you avoid having any virtual functions whatsoever in your component classes.
[/quote]
Typically you'd compose rather than inherit as in the above example, but sure, you could hard-code it in that way.

However, I think a lot of component-based systems strive for greater modularity than that, and also strive to be as data-driven as possible, which typically means 'assembling' entities at run time. In that context at least, a more general approach will probably be preferable.

Share this post


Link to post
Share on other sites
the inheritance-based approach says that a collider 'is' a transform and a renderable component 'is' a transform [/quote]

Every example I've seen on the subject suggests that composition is emulating an inheritance model. In the above example I called the classes Components just for illustration purposes.

The TransformComponent would actually be called MovableEntity (or some other). It is then fair to say that a CollidableEntity 'is a' MovableEntity which is then the base class for any CollidableEntities.

while the component-based method simply says that colliders, renderables, and transforms are distinct entities that have some knowledge of and interaction with one another [/quote]

The inheritance method does exactly this although it enforces any dependencies at compile time rather than at runtime. In the example above if one of your entities had a CollidableComponent but never had a TransformComponent then your program would crash.

which typically means 'assembling' entities at run time [/quote]

But every entity needs it own unique functionality, for e.g. if you attach a health component to some entity in your game it wont really do much other than take damage etc. You need some code somewhere that makes this data meaningful e.g. code that says: "if health < 0 then set object state to dead and set position to (x,y,z)". So in a component-based system where do you put this code?

Share this post


Link to post
Share on other sites

You need some code somewhere that makes this data meaningful e.g. code that says: "if health < 0 then set object state to dead and set position to (x,y,z)". So in a component-based system where do you put this code?


Either in the health component or in a seperate "control" type component depending on your needs. My health component "pulses" changes in health to the entity (which sends them to other interested components), they can in turn react to health changes if they need (an AI component would probably be interested). For killing entities, I have the health component itself do it directly (flag the entity as dead).

I really like using components/componsition over inheritance, its far cleaner and very flexable. Its data driven which is really nice, can construct a variety of entities with just a little bit of xml, no need for additional coding. When I think back to how my entities used to be (inheritance), I cringe at the thought. I'm trying not to let components directly use each other either, I read someone elses suggestion of components pulsing messages and tried it. Its working really well, very loosely coupled.

Share this post


Link to post
Share on other sites

Every example I've seen on the subject suggests that composition is emulating an inheritance model.

What do you mean? Can you provide a link to one of these examples?

I'm not sure what you mean about composition emulating inheritance, but composition is typically thought of as being an alternative to inheritance, not an emulation of it. Composition has different characteristics than inheritance, and addresses some of the issues that often crop up with the latter technique. In particular, composition offers greater orthogonality, which is one of the characteristics component-based systems exploit.

Can you clarify what you mean about composition emulating inheritance?

The TransformComponent would actually be called MovableEntity (or some other). It is then fair to say that a CollidableEntity 'is a' MovableEntity which is then the base class for any CollidableEntities.[/quote]
How about a tree? It's a collidable entity, but not a moveable entity (most likely). How would that fit into the inheritance hierarchy you describe?

The inheritance method does exactly this although it enforces any dependencies at compile time rather than at runtime. In the example above if one of your entities had a CollidableComponent but never had a TransformComponent then your program would crash.[/quote]
Component-based systems usually have mechanisms in place to ensure dependencies are present. In a language that supports exceptions, for example, you might simply throw an exception if a required component is missing. Another option is to add any required components automatically if they haven't been specified explicitly.

As for the fact that missing dependencies are detected at run time rather than compile time, that shouldn't be an issue. In a data-driven component-based system, entity definitions are data, just like models, textures, sounds, and game levels (this is one of the strengths of the system). It'll pretty much always be the case that a shipped game will rely on the accompanying data being correct and internally consistent. You wouldn't ship a game with a level with no spawn point, or a material that referenced a non-existent texture, or a sound file in an unreadable format, or anything like that. Ensuring that entity definitions include all the required components is no different; it's just part of ensuring that the game data is integral and internally consistent.

In other words, identifying missing component dependencies at run time is perfectly normal and is in keeping with how game data is handled in general.

But every entity needs it own unique functionality, for e.g. if you attach a health component to some entity in your game it wont really do much other than take damage etc. You need some code somewhere that makes this data meaningful e.g. code that says: "if health < 0 then set object state to dead and set position to (x,y,z)". So in a component-based system where do you put this code?[/quote]
It depends. It could go in the 'health' component if that behavior is common to all entities with health, or it could go in another component whose only responsibility is tracking the health and taking the appropriate action when it reaches some threshold, or it could go in a collision callback function, or it could go in a component associated with a 'mediator' entity that has knowledge of all entities with health, etc. There's really no one right answer to the question.

It's clear you're not sold on component-based systems, and no one's saying you have to be :) But, component-based systems work just fine.

If a concrete example would help, you might download the Unity engine and play around with it a little (it uses a 100% component-based entity system).

Share this post


Link to post
Share on other sites
Every example I've seen on the subject suggests that composition is emulating an inheritance model. In the above example I called the classes Components just for illustration purposes.[/quote]

Composition is NOT inheritance. Inheritance models an ' is a' relationship, for example, a Duck is a Bird. Where as the composition models a "has a" relationship, for example, a Car has an engine, has an exhaust etc.
Each component is its own object:


struct Engine
{
/* engine stuf here */
}

struct Car
{
Car();
~Car()
void Upate(float dt);
private:
Engine* m_Engine;
}

Car::Car()
{
m_pEngine = new Engine();
}

void Car::Upate()float dt)
{
m_pEngine->Update(dt /*, Any more params it needs */);
}

Share this post


Link to post
Share on other sites
[color="#1C2837"]Composition is NOT inheritance [/quote]

[color="#1C2837"]I know this, What I was meaning is that every example I've seen simply breaks down an inheritance hierarchy into component-form.

[color="#1c2837"]In your example you don't provide an interface for the components of your Car class therefore any external entity will not know that your Car actually 'has a' engine. Using an entity-component system you can query this by: car->hasComponent<EngineComponent>() and then act on the engine component of the car independently from the car class.

[color="#1c2837"]What I am suggesting is that you can use Virtual inheritance in C++ to mimic composition without having to add components dynamically and query them at runtime.

[color="#1c2837"]In this Example:
[color="#1c2837"]
class Car : public Engine
{
}


[color="#1c2837"]even though Car isn't an Engine you can consider the Engine to be a component of the Car. I suppose if they ever made a language to support composition it would look a bit like this:
[color="#1c2837"]
class Car
{
component Engine;
[color="#1c2837"]}

Engine* e = car.component<Engine>;
if(e) {
[color="#1c2837"]...
[color="#1c2837"]}

[color="#1c2837"]where the compiler could check dependencies between components at compile time.

Share this post


Link to post
Share on other sites
First of all, it seems misguided to me to make an entity's transform a component. What does it mean for an entity to not have a transform? Just make it a class member of the Entity type. Then you can pass it as a parameter to any components that need it.

Secondly, what you're trying to avoid, it seems, is querying your container of components for a particular one. I don't really blame you, it's somewhat bad form to be querying for the derived type of a base class pointer. I suggest you read up on the Liskov Substitution Principle. Basically, it says that you should use inheritance ONLY when a derived type can be used without the user knowing its type. In other words, instead of using inheritance whenever the object relationship conforms to "is-a", you should use inheritance only when the object relationship conforms to "is a substitute for".

The question to ask yourself is: Can Entity (and systems outside Entity that use an Entity's components) operate on the list of components without knowing about their types? The answer might be "no". In that case you're not gaining anything by the inheritance, so prefer to store your components explicitly. i.e.

class Entity
{
public:
CollisionData* Collision() const { return collision; }
RenderData* Render() const { return render; }

private:
Transform transform;
CollisionData * collision;
RenderData * render;
};


Common complaints:
"But that's so much typing!" -- You should not abuse design patterns to save typing. The above is a clean representation that succinctly describes an Entity.
"But now anyone depending on Entity also depends on CollisionData and RenderData" -- Rightly so, if they are going to be using those components. If they're not, you're free to derive Entity from an abstract class (maybe IEntity).

Now maybe the answer to "can Entity operate on the list of components without knowing about their types?" is "yes". That might be the case if you follow Nanoha's advice and use an message/event-based system. That might look something like:

// using string as the message type for simplicity.. it probably would be some kind of object hierarchy
class Entity
{
public:
void ReceiveEvent(string event)
{
// for each Component* c in components
// c->HandleEvent(event);
}
private:
vector<Component*> components;
};

class Component
{
public:
virtual ~Component() {}
virtual void HandleEvent(string) = 0;

private:
Entity * owner;
}

class CollisionComponent : public Component
{
public:
virtual void HandleEvent(string event)
{
if (event == "Collided")
{
// do some stuff
owner->ReceiveEvent("SomethingElse");
}
}
}



Note that now you have another design problem. Now you're switching-on-type when a component receives an event. You can alleviate this using some sort of observer pattern.

Share this post


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

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!