Do you understand "Evolve Your Heirarchy"?

Started by
12 comments, last by Antheus 16 years, 10 months ago
Hi, I wonder if anybody understands the object system Mick desribes in his article "Evolve Your Heirarchy"? :) Link: http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/ I've read it really carefully but I still don't know how to use it. A small example would be great.
Advertisement
I looks similar to a system Ive been working on where you build game objects from a set of more generalized building blocks instead of detailed 'deep' class trees.
The inheritance pattern is flatter, with specialized classes defining different specific abilities.

A tank would inherit its moving ability(behavior) from a 'vehicle move' class that would be inherited in parallel with a 'fires gun' or 'turret' class (armor class, warfare classs, etc..). Attributes and parameters modify the class's abilities as usual (though now they would be stored in a flatter attribute list data structure).

There usually is the problem of resolving conflicting classes and attribute needed for those abilities/properties. More complicated generalized code is needed to resolve those conflicts (since this system allows more combinations, the limited tree structure of a precanned 'deep' hierarchy has had conflicting elements avoided).


Example - the amphibious tank. Instead of defining a specific class branch for amphibious tanks, you would have the generic move classes 'move like a tracked vehicle' and moves like a boat' and assign both to your object. The object would inherit both abilities.


Consider that game objects have alot more properties to differentiate them and the combinatorics that are possible with only 10 or 20 primary 'ability' classes. In a 'deep' tree system you would have to prebuild the entire tree (and work out the inconstencies). In the mentioned system you could combine any classes that you wanted and they would have enough logic built into them to detect conficts and either resolve them or notify the user that they are impossible.

Example - conflicts. The classic Protection vs Armament Vs Speed problem for tanks and warships. A 'well armored' class would try to appropriate a sufficient chunk of the objects resources (mass/volume...) to match its definition. Likewise Armament 'Powerful Gun' takes up machinery space,m weight, volume for amunition, etc.. and the Speed component if 'Fast Moving vehicle' class is selected it would require a high HP to weight ratio -- enough space and weight allowance for the large engine/fuel/etc.. You cannot select all three to be 'optimal' as their attributes/limitations conflct with each other.
Logic would be put into each of the classes which would be called at an 'evaluation' phase (after all the classes were chosen) to calculate and detect conflicts by comparing requirements and attributes set by the classes.


You likely want a way for the designer to give partial abilities from different classes but wants some emphasised more than others. Many RPG systems have point allowances for a particular character 'level'. In the building block system you would apportion how much the character' being created leans towards certain professions (being aware that some abilities will suffer because of the compromises demanded by the conflicting classes). Generally you dont usually get a 'world class' pickpocket from someone who is a ham fisted brawler half the time.

In a realistic system everything is generally a compromise one way or another because an object that is too specialized (to maximize its efficiency doing one thing) is usually vulnerable to other unaccountedfor factors -- rendering it almost useless under varying conditions (ie- a slow armorless tank with a huge gun OR a tank that is solid armor and a popgun which hardly moves OR a fast popgun toting foilwrapped tank. real tanks are a compromise of all three sometimes with a preference on one moreso than the others)


The system I have been working on is for defining behaviors for an AI system where the inherrited classes include sets of solutions for various problems (procedures to accomplish tasks) and is for defining people which are significantly more complicated than mechanical objects (and thus why I would want to avoid 'deep' trees ...) 20 tanks that look/operate the same in a game is expected. 20 people that way in a RPG is absurd and boring.



Edit-

Such a system could also allow a 'degree' of membership to a class with allowances for it requirements and how much it can compromise between other classes. The system is more complex to impliment, but offers more fluid combinations.
--------------------------------------------[size="1"]Ratings are Opinion, not Fact
It's topic that I'm very interested in. The flexibility of the component based architecture is intriging. And something that I think we'll be seeing in more and more games in the future.

There is precious little information on the internet regarding the component-based design. Most google searches will turn up COM information, or other useless hits. This site has the most, and best info on the net. If you search here you can find a couple good discussions.

The single best source of info on the subject can be found in "Game Programming Gems 5" section 1.3 by Rene Bjarnes(sp) It contains an excellent explaination of the hows, and whys of of the component based design. But more importantly, it has a great bit of source code for a framework that will get you started.

I'm slowly getting my game working using this framework as a baseline.

I'll keep checking this thread to see what develops. It's a very fun way to program, and a good tool to use when your game needs a lot of flexibility.

Basically it is saying to get rid of the hierarchy. Inheritance hierarchies are not a good way to design complex game entity systems. Composition can provide more flexibility in a more data driven way. In a language like C++, inheritance would still be used to provide a common interface for the components, but game objects would all be component containers instead of having a separate class for each game object.

There could be an interface to allow components to send messages to other components via a publisher/subscription system or message queues, or components could be allowed to get pointers to each other if lower processing overhead is necessary.
Related read:
http://www.devmaster.net/articles/oo-game-design/
Every time you implement a singleton, God kills a kitten. Please, think of the kittens!
I'm at ease with the idea of limiting inheritance and instead use separate components, but what I wonder is how to implement it. Should the components reside in many different managers and we update each manager one at a time and then for example the "movable" object affects its linked "position" object? I would like to see some rough pseudo code for a small example.
Quote:Original post by eczkchtl
I'm at ease with the idea of limiting inheritance and instead use separate components, but what I wonder is how to implement it. Should the components reside in many different managers and we update each manager one at a time and then for example the "movable" object affects its linked "position" object? I would like to see some rough pseudo code for a small example.


This is something I would be very interested in seeing also.

I figure each component has a list of the other components contained in the entity it is currently a part of. This is so it can affect the other components.
There's several aproaches to this, and multiple implementation strategies. Java went farthest on this, but then the general movement died down a bit, since often such aproach has proven simply too unwieldy for simple tasks or typical applications (usually web-based).

One simple concept is this:
//contractsclass Movable{  virtual void move( Vector3D destination ) = 0;public:  Vector3D m_currentLocation;}class Combatable {  virtual void onHit( Combatable attacker, CombatAction attack ) = 0;}class RigidBody {  virtual void applyImpulse( Impulse v ) = 0;  virtual void tick( double deltaTime ) = 0;  Vector3D velocity() { return m_velocity; }  Vector3D rotation() { return m_rotation; );private;  Vector3D m_velocity, m_rotation;}class Collidable {  virtual void testCollisions( void ) = 0;}class Physical : public RigidBody, Movable, Collidable{}// Entitiesclass Player : public Physical , Combatable { // implement functions }class DecorativePlant : public Collidable {  // implement functions}class Missile : public Movable, RigidBody, Collidable {}


Now, how to process them.

The simple aproach is to keep them in a list or a tree. Then you can apply the visitor pattern on the type you're interested in.

Visitor looks something like this:
class PhysicalVisitor {public:  void visit( Physical *m ) {    Vector3D newLocation = m->m_currentLocation * m->velocity() * deltaTime;    m->move( newLocation );  }private:  double time_delta;};class CombatVisitor {public:  void visit( Combatable *c ) {    if ( action->canAffect( c ) )      c->onHit( attacker, action );    ]  }private:  Combatable *attacker;  CombatAction *action;};


Note, there are no objects, only policies and contracts. A potted plant can be combatable or not. It can even be Movable, and walk around if we choose to.

Now to processing:
class ComponentManager {  template < class Visitor >  void doMovement( Visitor &visitor ) {    for every ( Movable *m ) {      visitor.visit( m );    }  }  template < class Visitor >  void doCombat( Visitor &visitor ) {    for every ( Combatable *c ) {      visitor.visit( c );    }  }  template < class Visitor >  void doRender( Visitor &visitor ) {    for every ( Renderable *r ) {      visitor.visit( r );    }  }};void doCombat( ComponentManager *manager, .... ){  Attacker *attacker = ....;  CombatAction *action = .....;  CombatVisitor visitor( attacker, action );  manager->doCombat( visitor );}


And that's the general idea.

You define objects by compositing them from different policies and contracts, and manager is responsible for applying actions performed on objects that are capable of performing them.

There's plenty of issues though. First is the exact implementation. From pure inheritance, to heavily templated code, to a compromise, to IOC, each has good and bad sides, but they are beyond the current scope.

Then there's dependencies. How do you manage them? Define new policy? Bypass the component model? Once again, lots of compromises.

Order is another issue to consider. Adding objects to manager in some order may not guarantee processing in the same order.

Also, how do you have policies that need to be performed in a sequence for each object. So rather than doing doMove on all objects, followed by doRender on all objects, what if you need to do (doMove + doRender) on all objects.

But the general idea is that rather than inheriting from some based object, and refining functionality, in-game objects are a sum of parts.
The way Java uses interfaces like that is exactly what is wrong. There should not be a "decorative plant" class. There should not be a separate class for every type of entity.

The idea is to design a component interface that is implemented by all components. Actually, the idea proposed in that article is to not even have entity classes at all, just a component manager and various components. There is no "movable" component. Any component that moves an object is just connected in some way to a position or physics or whatever component through which it accomplishes the movement. Some components might receive events that trigger them to affect other components, etc.
Quote:Original post by Vorpy
The way Java uses interfaces like that is exactly what is wrong. There should not be a "decorative plant" class. There should not be a separate class for every type of entity.


Yes, but keep in mind, that the holder class which inherits the functionality is here just for the storage purpose in ComponentManager.

There is no need for an object to be composed using inheritance. The visitor doesn't know about containment, it uses merely functional parts. This is why I didn't define storage withing ComponentManager. They could be stored in one list, in separate lists, in functionality/implementation hash map, or even use other services to obtain it.

Quote:There is no "movable" component.


I didn't see any indication of that in the article. The graph even indicates there's a clear distinction on what functionality components have.

Quote:Any component that moves an object is just connected in some way to a position or physics or whatever component through which it accomplishes the movement.


This "some way" scares me. How exactly are they connected then? By arbitrarily holding pointers to each other and invoking arbitrary methods?

Component based aproach still uses interfaces to define functionality. It just doesn't require inheritance to enforce it. The reason one inherits is simply to define the interface, not to re-use functionality. In C++ templates make this very effective, since implementation and inheritance is irrelevant.

The visitor aproach above implies sequential processing, but it does clearly decouple the function from the form. Our visitors can be anything, and they are applied to some clearly defined component.

This can obviously be extended beyond such simplistic aproach, but defining a system with no clear interface or interaction seems simply too unwieldy to me.

Or am I missing something, and is there a completely different methodology of implementing the components and interactions between them?

This topic is closed to new replies.

Advertisement