Sign in to follow this  

Do you understand "Evolve Your Heirarchy"?

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

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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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:

//contracts
class 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
{
}

// Entities
class 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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
There's basically 2 ways to implement this sort of architecture.

1) All components inherit from a base class which gives a basic interface for storing the components. Something like a couple pointers for a linked list node.
Then, your Entity class will contain this linked list of components. This is a very easily-to-grasp method of managing your components. They're just stored in the entity that they comprise.
The downside of doing it this way is that it isn't very easy to "find" a particular component. When you need to send a message to a component, you end up iterating through many entities, even if they don't contain a component of the type that accepts the message. It's still a viable way to keep track of your components. And if your entities are all very similar, this may be the best way to go.

2) The Database approach. (no, not kind of database) This is the way it was done in the Game Programming Gems 5 example. And it's the method I prefer.
Basically, components are stored in a series of datastructures which are focused much more around the Components, not the entities.
I don't have my GPG5 book here, so I can't detail all the datastructures they hold the component pointers in. But they are mostly things like a std::map of std::list, so you can search the map for components of a certain type, and then iterate through the list sending messages to all the components which can receive that message. All components have an "Entity ID" member variable, but that's the extent of thier association with a particular entity. The cool thing about this approach, is that there _is_no_entity_ entities are nothing more than ID numbers stored in various components.

This method seems better to me, but I can see some of the benefits of the entity-centric approach. The good thing is, if I decide that I need to hold on to an entity for some reason, I can always make one, and just add that as a new data-structure in the database object.

I'm sure this has been very confusing, especially the method #2 explaination. If you can get your hands on the GPG5 source-code or book, it does a MUCH better job of explaining the architecture than I ever could.

Share this post


Link to post
Share on other sites
I believe method 2 described by Nohup is what the article was talking about, and it is more general than the entity-centric approach. The problem with using interfaces and then having your components inherit from the interfaces they support is that you're kind of stuck with blobs. I'd rather have a more data-type oriented interface between components, or dependency injection.

For example, Component A holds a pointer to a class that takes a vector as input, and Component B implements that class (in C++ maybe templates are used to avoid the virtual method call). Component A and Component B are both part of a certain entity, so they are created and A is connected to B.

Maybe B is this 'movable' component, and it takes a vector and adds it to a position component that represents the location of the entity, and A is an component designed to output a series of rotated vectors. Now the entity spins in circles.

Or A could generate vectors based on user input (either keyboard or mouse, maybe a flag to control this or separate components), and now the user controls the entity. Or maybe B is part of a physics system and accumulates a force, or sets acceleration, or selects an adjacent component in a gui.

Now we decide that this entity doubles its speed when a certain game state is active. Throw a component in between that doubles the input vector and then outputs it to the next component when a certain state is in effect. Maybe this component can be parameterized with a factor and a triggering condition, or something more or less general, depending on taste. Maybe the factor can even be controlled in game by attaching another component, or using debug tools, to allow the effect to change or the designer to try different values.

The component system can provide some standard systems for more flexible communication, like broadcast event channels, or these could be implemented as other components.

Share this post


Link to post
Share on other sites
Quote:
Original post by eczkchtl
Ok, so now we've got some different explanations. I'll see if I can get hold of the GP Gems 5 article, maybe it will clear things up a bit.


You really don't. I checked with GPG5 before giving my example.

The difference is in how the interfaces are stored. In my case I used inheritance to determine which interfaces are supported. The GPG5 example uses containers that provide the interfaces.

The Movable (IMove + IMoveHelper in book's example) is perfectly valid (that's incorrect in the above post).

The key to component based design, regardless of how you organize your structures is the following: Interface which defines functionality and a Component manager system which provides connections between references.

When performing actions you're performing them on interfaces, not objects. These interfaces could be implemented by single instance, by separate instances or by some static facilities. But component based design de-couples the notion of functionality being tied into a specific inheritance tree.

As such, anything that implements certain functionality via some interface can serve as perfectly valid building block - opposed to inheritance where in order to implement a player, you might need to inherit from a Creature object, and all that such object has, including AI, spawn mechanisms, etc. Conversly, Creature object inheriting from Player would have keyboard input support, chat system, quest log, scores, etc...

In my example above, the classes Player, DecorativePlant and Missile are *never* used by game systems. You can never request an instance of Player or Missile. They are merely allocated at some point somewhere deep in component manager merely as a convenience for this particular storage.

All other interactions are performed solely on the interfaces used by these classes.

The visitor patter provides the most basic and generic method of processing all these interfaces in sequential fashion. If you extend the rules for interfaces and how they interact with other components, possibly by adding them an ID, or allow them to hold references to other objects, then you do not need to use it anymore.

Share this post


Link to post
Share on other sites

This topic is 3840 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.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this