Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length

 
Advanced search

877817 Posts in 32884 Topics- by 24319 Members - Latest Member: NotoriousPyro

May 20, 2013, 01:50:58 PM
TIGSource ForumsDeveloperTechnical (Moderators: Glaiel-Gamer, ThemsAllTook)Component-Based Architechture: Getting Away from Inheritance
Pages: 1 2 3 [4] 5
Print
Author Topic: Component-Based Architechture: Getting Away from Inheritance  (Read 13551 times)
jotapeh
Level 10
*****



View Profile WWW Email
« Reply #45 on: January 20, 2010, 05:08:00 PM »

I'd just like to say I really appreciate the OP.

I'm not sure if it's 100% how I'd like to do things, but it's very nice to have this approach explained in such a clear manner. I'll definitely be trying to figure out how to use the strengths of this approach in my projects.

Cheers and thanks for a good write-up.  Beer!
Logged

Mikademus
Level 10
*****


The Magical Owl


View Profile
« Reply #46 on: January 23, 2010, 05:08:06 AM »

Summing up the problem: You want (1) flexibility enough to compose the behaviour of your entities, while not incurring (2) run-time overhead or code maintenance problems. Using inheritance or multiple inheritance will be efficient runtime-wise but scale poorly. Using a component architecture will in a large-scale application with a rich set of components and densly combined entities scale nicely but be increasingly expensive, eventually prohibitively so.

In a sophisticated C++ design this is a non-issue. If you accept defining your behaviour in code (as opposed to external scripts) you can have the full flexibility of components with no runtime overhead if you use policy-based templates, as per A. Alexandrescu (2009). As with all templates this may increase compilation times.

So we want flexibility about movement, attacking and interactions, or whatnot. Say we define an entity as

Code:
template<typename MOVE, typename ATT, typename INTER>
class Entity : public MOVE, public ATT, public INTER
{
   ...
};

You can specify any number of policy classes and combine these to your hearts' content:

Code:
struct SessilePolicy
{
    void move(double deltat) {}
};

struct MovePolicy
{
    void move(double deltat) { RunSomewhere(); }
};

struct PacifistPolicy
{
    void attack() {}
};

struct PsychoPolicy
{
    void attack() { GoBeserk(); }
};

struct ShootLaserFromEyesPolicy
{
    void attack() { EatThis(); }
};

struct NoninteractablePolicy
{
   void interact() {}
};

struct ChattyPolicy
{
   void interact() { Eliza(); }
};

Now you can compose your game entities

Code:
typedef Entity<SessilePolicy, PacifistPolicy, NoninteractablePolicy> DummyEntity;
typedef Entity<MovePolicy, PsychoPolicy, NonteractablePolicy> GenericEnemy;
typedef Enity<MovePolicy, ShootLaserFromEyes, ChattyPolicy> ExampleFromPreviousPost;

ExampleFromPreviousPost myWeirdHouse;

In short, we live in a richer world of architecture than just having to choose between inheritance or components. Not many people code in Smalltalk nowadays, but this language separates inheritance of types from inheritance of implementation, and just as Objective-C uses signalling to communicate. Less frequently used languages, but a fourth architectural option.

Template composition of policy classes is one way to achieve the same results as components but without sacrificing runtime efficiency or code manageability, while retaining the full type-safety and debugging tools of C++, which you wouldn't have in scripting.
Logged

\\\"There\\\'s a tendency among the press to attribute the creation of a game to a single person,\\\" says Warren Spector, creator of Thief and Deus Ex. --IGN<br />My compilation of game engines for indies
BorisTheBrave
Level 10
*****


View Profile WWW
« Reply #47 on: January 23, 2010, 05:36:39 AM »

Multiple inheritance like that is not ideal either. It is not what policies were designed for. The way you've designed it, there's no root Entity class, which makes it basically useless for games (unless you go even further down the template path). Nor is there any dispatching or interfaces, so that at runtime I can call the correct move method.
You can fix these problems without much fuss with virtual inheritance, but then it starts to resemble component architecture much more than I think you were intending.
Logged
Mikademus
Level 10
*****


The Magical Owl


View Profile
« Reply #48 on: January 23, 2010, 07:45:55 AM »

Multiple inheritance like that is not ideal either. It is not what policies were designed for.

Hmm? That's exactly what policies were designed for. Ok, let's define it a little bit more clearly:

Code:
struct Entity
{
    virtual void move() =0 {}
    virtual void attack() =0 {}
    virtual ~Entity() {}
};


template<typename MOVE = DefaultMovePolicy, typename ATTACK = DefaultAttackPolicy>
struct ComposedEntity : public Entity, public MOVE, public ATTACK
{
    void move() { MOVE.move(); }
    void attack() { ATTACK.attack(); }
};

What we have here is a class ComposedEntity that fulfil the Entity interface and can thus be used polymorhpically. Since you will use it polymorhpically you've already accepted that all principal methods must be declared in an interface as well as paying the slight overhead of a vftable (which however is much less an overhead than an extensive component architecture will incur).

The class will fulfil the Entity interface. It will always have the required methods. All without "having gone further down the template path" (which you make is sound like a bad thing, btw? Templates used judiciously--as any language facility should be used!--are a boon for games development) or using virtual inheritance, which is a tool for resolving the diamond of inheritance, and how that would resemble components is an obtuse claim...
Logged

\\\"There\\\'s a tendency among the press to attribute the creation of a game to a single person,\\\" says Warren Spector, creator of Thief and Deus Ex. --IGN<br />My compilation of game engines for indies
Klaim
Level 10
*****



View Profile WWW
« Reply #49 on: January 23, 2010, 01:27:43 PM »

In the current C++, you have no way to specify an undefined number of types.

Using policy is not as flexible as using components because you cannot easily add components. Components have to be orthogonal "aspects" of the same object. Sometimes they are interdependant but it's component-relative, not object-relative.

So using policy here is almost the same as using multiple inheritance. You get the interface exposure but you cant easily make changes to code.

Anyway, using policy is just about choosing polycy of behaviour, not "aspects" of the objects. I'm not sure I'm clear here...
Logged

http://www.klaimsden.net | Game : NetRush | Digital Story-Telling Technologies : Art Of Sequence
BorisTheBrave
Level 10
*****


View Profile WWW
« Reply #50 on: January 23, 2010, 04:15:03 PM »

Well now you've changed what you are doing. In the first example, I thought you were inheriting Entity::move from MOVE. Now you are not. In fact, you are not really using any features of inheritance once you've done that, you might as well have written:
Code:
template<typename MOVE = DefaultMovePolicy, typename ATTACK = DefaultAttackPolicy>
struct ComposedEntity : public Entity
{
    MOVE move;
    ATTACK attack;
    void move() { move.move(); }
    void attack() { attack.attack(); }
};
no?

Also, Klaim makes some good points.

Anyway, don't get me wrong, I love templates. They can just explode in complexity if you think you can recreate every runtime feature at compile time.

In my mind, virtual inheritance is creating multiple subobjects, and linking them together with pointers (as opposed to laying them out in fixed relation to each other in memory with regular inheritance). Certainly seems like components to me.
Logged
Mikademus
Level 10
*****


The Magical Owl


View Profile
« Reply #51 on: January 24, 2010, 05:34:50 AM »

Ok, I'll admit I designed my example poorly and thus didn't make a very good case for policies. And as given, you can rewrite my source as you did. However, when writing policies each policy class generally also implements an interface, and one thing you can't do when rewriting like you did is iterating over a set as instances of those policy interfaces. However, if you take this to its full extent, where classes don't need to have all policies, you will need to test for their existence dynamically and you will have created a statically defined component model with some run-time testing overhead.

Nonetheless, I should have given a better example. Still, mutatis mutandis the architecture I suggested, and *proper* policies Wink, is a good compile-time alternative to a dynamically composed component architecture. If the examples above look quite like components, then that is probably because they really are quite similar, the difference being that the templated version (if well designed) will not entail run-time tests.
Logged

\\\"There\\\'s a tendency among the press to attribute the creation of a game to a single person,\\\" says Warren Spector, creator of Thief and Deus Ex. --IGN<br />My compilation of game engines for indies
Klaim
Level 10
*****



View Profile WWW
« Reply #52 on: January 24, 2010, 06:11:20 AM »

Once C++0x is here and usable, we can use templates to statically setup components by using template variants.
Logged

http://www.klaimsden.net | Game : NetRush | Digital Story-Telling Technologies : Art Of Sequence
powly
Level 3
***



View Profile WWW
« Reply #53 on: January 24, 2010, 06:55:07 AM »

Once C++0x is here and usable, we can use templates to statically setup components by using template variants.

C++1x, you mean ;>

But all this discussion seems kind of confusing to me, I'm just using classes to represent things and sometimes things inside things.. I've never had an actual need to inherit, not to mention use some component stuff.
Logged
Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW Email
« Reply #54 on: January 24, 2010, 10:22:33 AM »

Once C++0x is here and usable, we can use templates to statically setup components by using template variants.

Do you mean variadic templates?  I've been following C++0x very closely, and have never seen "template variants."

This:
Code:
template <typename... Components>

The possibilities offered by these babies are staggering.  GCC has them now, if you want to play.
Logged

Franchise - The restaurant wars begin!

What would John Carmack do?
Klaim
Level 10
*****



View Profile WWW
« Reply #55 on: January 24, 2010, 10:31:17 AM »

That occurs only in some kind of systems where you need some objects to have common base class because they are all under the same "laws" of the game. Often all the movable objects in a game share a common base.

Now the idea is that instead of using inheritance that creates a strong relationship between classes, you simply "compose" your game object types with "aspect/behaviours" that are components.

That's easy when you just compose a class the usual way:
Code:
class Player
{

  GraphicEntity m_gfx;
  PhysicEntity m_physic;
  LivingEntity m_lifestate;
};
So now the problem is more about "exposing" the components to the "surface" of the type, allowing external system to manipulate specific aspects of the instance of this type.
In fact, in C++ it's hard to expose interface without inheritance, so it seems better at first glance. But it's a bad solution as stated before.

In ActionScript3 you can do this without inheritance, because reflection allow you to "scan" the members of the object, so a simple naming rule for components could be enough.

In C++ however you'll need something as exposed in the first message to allow the system to access the components dynamically with a clear interface to the world.

Now I say that because I used a similar system in an aborted project long time ago and I see the benefits. But the game I'm working on today don't need at all this system. It's not always appriopriate. It is when you have to separate different aspects/behaviour from the entity type. If you already know that there is no way a door can speak in your game and your class hierarchy is fixed or there is no class hierarchy, you shouldn't bother. It's just a good technic to know when you're making a systemic game world. Almost any RPG is.

Quote
Do you mean variadic templates?  I've been following C++0x very closely, and have never seen "template variants."

Ah yes sorry it was late when I posted, "variadic templates" are what I was thinking about.
Logged

http://www.klaimsden.net | Game : NetRush | Digital Story-Telling Technologies : Art Of Sequence
jotapeh
Level 10
*****



View Profile WWW Email
« Reply #56 on: January 24, 2010, 06:34:04 PM »

In ActionScript3 you can do this without inheritance, because reflection allow you to "scan" the members of the object, so a simple naming rule for components could be enough.

Can you expound upon this further? I'm not understand quite what you mean by "Reflection". Please help a hopelessly oblivious fellow  Who, Me?
Logged

zantifon
Level 0
**


View Profile WWW
« Reply #57 on: January 24, 2010, 11:57:15 PM »

Reflection is the ability of a programming language to inspect and modify it's own behavior. In this case, he is referring to the ability to query the type of an object at runtime. So you could store the components as a basic list of objects. Then, at runtime, iterate over then and get the name of the type as a string and do logic based on what the string is. Many languages (including AS3) have reflection, but C++ does not (well, it does, sort of, but it's not as useful and largely not cross-platform). This strategy would work with any language with reflection.


I have to say though, I think keeping components in a list is a bad idea to begin with. In my designs I don't even have an Entity class at all. Entities are simply identifiers (like an int or something). Then for each component type, you have a map from entities to components. Entities are defined simply by whichever component maps have entries for that entity. It's almost like a relational database.

Then you don't need to do any kind of reflection or any other RTTI or template hacks or whatever. If you want to get the component for a particular entity, query the map. If it doesn't have one it won't be in the map. If you want to iterate over all components to do logic, just iterate over all the maps. You'll process things in a different order, but I've found this to actually be easier to work with then processing one entity at a time.
« Last Edit: January 25, 2010, 12:20:24 AM by zantifon » Logged
Klaim
Level 10
*****



View Profile WWW
« Reply #58 on: September 24, 2010, 08:21:13 AM »

(sorry for the necromancing but it seems important)

I just hit this question/answer that got me to this blog post that is really interesting on the subject of Component-Based Architecture. Leads also to Functional-Reactive Architecture that I didn't heard about before!

Thought people here will be interested.
Logged

http://www.klaimsden.net | Game : NetRush | Digital Story-Telling Technologies : Art Of Sequence
rogerlevy
Guest
« Reply #59 on: September 26, 2010, 05:37:05 PM »

My game engine uses a component-based architecture.  I think I'm going to be reading this thread throughout the coming week.  I want to see if I can get more smarts on the topic Tongue
Logged
Pages: 1 2 3 [4] 5
Print
Jump to:  

Theme orange-lt created by panic