Sign in to follow this  
aeroz

Component based system

Recommended Posts

I have implemented a kind of component based system in my game. My game entities consist of components, each with its own functionality. When following this article (I will reference to this article, maybe you have to read a small part of it), my system is at the stage "OBJECT AS COMPONENT CONTAINER". Thus there are game entity objects in my game. Example:
// very simplified
class GameWorld
{
public:
    AddEntity( pEntity );

private:
    EntityMap m_entities;
};

class Entity
{
public:
    id GetId();
    comp GetComponents( compId );
    SetComponent( pNewComp );

private:
    id m_Id; // unique identifier for this object
    ComponentMap m_components;  // map of all components
};
What's exactly the benefit of having an "OBJECT AS A PURE AGGREGATION" instead of this? I don't see why it should be better. I once saw an implementation of this "pure aggregation" (in the Game Development Gems). There was something like a component manager but it had internally a container for each entity. What's the difference of having an independent game object and having containers of components inside a manager? My code looks like this:
// for me this is "OBJECT AS COMPONENT CONTAINER"
comp->GetEntity()->GetComponents()
The same thing with the other approach would maybe look like this:
// for me this is "OBJECT AS A PURE AGGREGATION"
componentManager->GetComponentsByEntityId( comp->GetEntityId() );
Thanks for the help!

Share this post


Link to post
Share on other sites
If I get what he means, the advantage would be this:

As long as you still have that "Entity" object, its possible that functionalities will creep out to it, making it more than just a sum of its component. As time go by you will see it getting bloated. It may not happen with 1 or 2 disciplined programmers, but in a large team with deadlines and all, its inevitable. Indeed, Ive worked with such architectures (object as component container) and Ive seen it happen.

I might add that I love working with a component architecture. It makes many things like serialization and code sharing much easier than with any other architecture Ive seen, and the code is incredibly cleaner. Limiting dependencies between components has been an issue, tho not an overly concerning one.

Share this post


Link to post
Share on other sites
Do you mean by "functionalities will creep out to the entity" that more and more code is put in the Entity class? What if you just say that changing the entity class is forbidden? Would this solve the problem?

Thanks for the reply

Share this post


Link to post
Share on other sites
Quote:
Original post by aeroz
Do you mean by "functionalities will creep out to the entity" that more and more code is put in the Entity class? What if you just say that changing the entity class is forbidden? Would this solve the problem?

Thanks for the reply


Yeah, I meant that more and more code will be put into the Entity class. As for "changing" the entity class is forbidden"... You'll find that "forbidden" and "mandatory" are very loose words when making a game. They get looser the bigger the team and the budget gets. "Impossible" is a much better argument than "forbidden" when the producer ask you to make a dirty change before 5 o'clock ;)

At any rate, there is no Code Police (yet), so in the end you just pick what suits your team best. If you think pure aggregation is too much trouble, there's nothing wrong with that.

Share this post


Link to post
Share on other sites
Quote:

Do you mean by "functionalities will creep out to the entity" that more and more code is put in the Entity class?

All entities have a matrix right? lets just put that in there.
Seems useful to know the parent of this entity, lets put that there.
Message dispatching? lets put that in there.
etc.

Quote:

What if you just say that changing the entity class is forbidden? Would this solve the problem?

We have 6 days (or 6hrs!) to get a disk to the publisher so we can get payed. Is it faster to make new components or just modify stuff that is already there? Oh hey look! An "entity" component i can put stuff in! Oh that was an entity class not a component? oh well. its there now.
Deadlines are always going to just make things harder on you. "forbidding" something only works when people have time to respect those rules. "Proper coding" comes second to "functionally coded" code when a deadline is close. The end user doesn't know how nice or ugly your code is, only the feature set you present and the bugs that still exist.


You should also be warry of components that do too much. It is tempting to write a "zombie" component the encapsulates everything.
Where something that is a collection of behaviors makes it easier to create new enemies that are all similar with slight differences.
You could subclass a "sensor" component to give entities the ability to "sense brains" and "sense sound".
You could subclass a "move" component to give entities the ability to "shamble" "run" "crawl", all of which would be asking "pathing" components for where to go next.
You could tie together all of the "sensors" "move" "animation" "mesh" "etc." components with a "zombie AI" component that only knows to look for sensors, and convert that to move commands.

Adding "sense smell" at that point is as trivial a making and installing a new "sensor".
Adding a "flying" zombie is as trivial as installing a new "move".
Adding a "scared human" is as trivial as swapping out the "zombie AI" component.

Sure you have a lot of interdependence in different component modules that way, but there is also a lot of freedom for a designer to swap out parts and try to make something new. That "something new" might have interesting and different bugs from the interactions, but the fixes are more flexable than fixing the same bugs over and over in your "omni-zombie-component" and "omni-human-component".

It is also tempting to go overboard on the seperation. You need to find a balance, or your components will spend more time looking up components to talk to than they will do actual work. In my above example, "walk" "shamble" "run" "craw" should not just be different animations and speeds. You could do that in one component. "Walk" would be looking for free areas to go. "Shamble" might look for a free area to walk, then adjust the path to find interesting things to bump into. "Run" might look for free areas above knee height, and having a chance to hop over or trip on any low to the ground obsticals. "crawl" might including being able to climb over obsticals that the other movements wouldn't, and prefers that to being in the free and open paths.
If you can't make the component do enough unique work, see if it really does belong in another component.


[Edited by - KulSeran on February 20, 2010 12:09:11 PM]

Share this post


Link to post
Share on other sites
Hi aeroz,

Here is a few of my thoughts.

I would like to use the term "OBJECT AS A PURE AGGREGATION", although they function in the same way. I think a pure object is like flyweight pattern. As a game object, all it has is just a list of components. So it's better to manage them in a master class, the object manager. I did this in my code.

also, to facilitate this kind of use,

componentManager->GetComponentsByEntityId( comp->GetEntityId() );

I encalpulated a GOHandle class, whose idea is from sharedPtr. This class only holds a pointer/id/handle to a particular object, and then any operation on that will be delivered to the object managers. This way looks more decent and easy to understand to my sense.

So, the code above will become like this,

GOHandle entity(comp->GetEntityId());
entity->getComponent();

I think these are no big difference, just a pattern of programming.

Hope it help

hao

Share this post


Link to post
Share on other sites
Hey!

Thanks a lot for your posts, very informative!

I'm not a professional programmer and have never developed in a team so I have never faced this problems. But your arguments make sense to me.

I still don't know if I should change my system to this "aggregation type".
Quote:
At any rate, there is no Code Police (yet), so in the end you just pick what suits your team best. If you think pure aggregation is too much trouble, there's nothing wrong with that.

As I'm alone in my project I think I will keep it the way it is now.

Have you ever read about the Nebula3 engine? I just looked at a development snapshot and it looks like their system has Entities as objects. They have an "EntityManager" like I have my GameWorld.

@wuh84: I have never used the "Flyweight Pattern", but I just read about it and I think you can use this pattern independently of whether you have a master class for components or a master class for entities. Well, it would be easier when the component manager is the Flyweight Factory, but it could also be made separately (just with boost.flyweight maybe?).

Regards

Share this post


Link to post
Share on other sites
Generally strictly component based systems are needlessly complex and sometimes slow. You can lump a lot of components together into a particular type such as a base character, derived npc or types of characters. But ultimately once you know an object is a character, you shouldn't have to search for too many other components to get what you need out of it.

A system like this is pretty common and imo favorable. You can derive the entity from a ComponentContainer if you want to preserve the pure component capablities.

struct Entity
{
BrainPtr Brain;
PhysicsPtr Physics;
GraphicsPtr Graphics;
};

With some kind of RTTI in place, you can check to see if those main components are the intended derived components, such as character physics, character brain, vehicle brain, etc.

Your brains can be assembled with components (not in the generic component container sense, just in the componentized code sense) or with inheritance to reuse functionality.

Share this post


Link to post
Share on other sites
I have implemented "OBJECT AS A PURE AGGREGATION" myself and I thought, that its really usefull. My code is now really transparent and easy to use. I can adding models very easy only by editing couple of lines.

I arranged Component Manager like this:
List<ObjectLocation> (contains Position, Scaling and Rotation of object)
List<ObjectCollision> (contains another List of BoundingBoxes)
List<ObjectGraphics> (contains Vertex and Index Buffer of object)

Some of those Lists I convert to array after adding objects for faster iterating and faster indexing.

and I can easily update all ObjectLocatiopns by iterating through List... and position [0] is object 0, [1] is object 1.. so i can easily access all settings from certain object by the same index in all Lists

If certain object doesnt have, lets say, collisions, I insert null to the list, and then if i iterate i make simple test if (collicion == null) continue

Its little bit more complex and still not in final stadium in my engine (gor example Render calls shouldnt really be inside ObjectGraphics, loading objects is mess - but its done only during load, so i dont really care for now). Before i change main part of my engine to this, i had eg. 220 FPS on scene (all was written in one huge Render loop)... now with same scene and same settings a got about 215 - 220 FPS... so power drop-off is not so bad according the code is almost perfectly clear...


Share this post


Link to post
Share on other sites
The article you mentioned should only concern novice programmers.
It is common sense objects should not be limited to inherit features from only 1 object.
In C++ you can implement the "Component based system" using multiple inheritance.

Share this post


Link to post
Share on other sites

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