Jump to content

  • Log In with Google      Sign In   
  • Create Account


Outboard component-based entity system architecture


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
106 replies to this topic

#1 Sneftel   Senior Moderators   -  Reputation: 1776

Like
0Likes
Like

Posted 09 September 2007 - 03:43 PM

I've recently been making an entity system. Initially, I was planning it around a component-based model:
class Component {
public:
    virtual void OnUpdate() = 0;
    ...
};

class Entity {
    ...
    std::vector<shared_ptr<Component> > components;
    ...
}


In this model, the Entity aggregates a list of heterogeneous components, which modify the behavior of the object without having to complicate the Entity class. The factory in charge of creating Entities is responsible for populating them with the appropriate Components. There have always been a few things I didn't like about the component-based model. * Components have difficulty finding other components in the same Entity. Given an AnimationComponent which requires a PathfindingComponent to use, often it's necessary to manually iterate through the list of Components, dynamic_casting each one until you get a hit, or else to complicate the factory with details of what component needs to know about what, or the Entity with specific typed Component members for direct access. * Components have difficulty finding the same components in other Entities, and their associated game-wide subsystems. The PathfindingComponent needs to know at least about other Entities' PathfindingComponents, or (more likely) a GameWidePathfindingSystem. The former can require a lot of iteration and dynamic_casting; the latter requires either some pretty serious arrow chains, or singletons/globals. The basic problem, I think, is that an Entity has a big ol' list of components and no clue what to do with them. I've shown an Update event up there, but even that's a stretch; I've seen a lot of components with empty OnUpdate() methods. You can, of course, add OnDamage and OnNewPath and OnEntityScratchedLeftEar as necessary, but that makes the component-entity response matrix sparser and sparser. You can abandon that approach and have entities hook into the events they care about directly, but then what's the point of the observer pattern there at all? While designing my graphics system (this time through, enforcing a really strong logic/graphics separation), I fell into this alternate arrangement.
class FooComponent
{
    FooComponent(shared_ptr<Entity> entity);
    ...
};

class FooSubsystem
{
    void OnEntityAdded(shared_ptr<Entity> entity)
    {
        components[entity] = SomeComponent::Create(entity);
    }
    void OnEntityRemoved(shared_ptr<Entity> entity);

private:
    hash_map<shared_ptr<Entity>, shared_ptr<SomeComponent> > components;
};

class World
{
    Event<shared_ptr<Entity> > EntityAdded;
    Event<shared_ptr<Entity> > EntityRemoved;
};


I realize that's a bit skeletal, but what's going on there is that the subsystem hooks into events which fire when entities are added to or removed from the world, automatically creating corresponding components for itself and holding onto them. The same components as before are being created; but rather than being stored by the Entity in heterogeneous lists, they're stored by their parent subsystem in homogeneous lists. The thing I like most about this is that there's no need for the components to swim back to their subsystem. In fact, they generally don't need to know about the subsystem at all. More subtly, in the case where one component type (BarComponent, say) depends on another (FooComponent), the BarSubsystem can listen to the FooSubsystem for add/remove events instead of the World, automatically matched up to the component it cares about. In the case of the entity/graphics divide, doing something like this is just common sense. I've been using it for a lot more, though; pretty much all of the behavior of my Entity, which by now is a pretty teeny class. I'm sure others have explored this organization before. What say you all? [Edited by - Sneftel on September 10, 2007 1:12:22 PM]

Sponsor:

#2 vtchill   Members   -  Reputation: 180

Like
0Likes
Like

Posted 09 September 2007 - 04:38 PM

I decided to go down a very similar path with the component-based entity design. In theory it sounds like a fantastic idea with the entities really just being containers for a certain grouping of components. It really seems to promote the idea of modularity and seems like it would adapt very well to data-driven designs. When I first thought about this design it made a lot of sense and it just seemed natural to me that an entity really is just a collection of components and it does nothing more than hold on to them. I also liked the component-based design because it seemed a good solution for the deep entity hierarchy problem. After saying all of this though, it is just like any other design decision and it comes with its pros/cons.

Once I started implementing I ran into the same questions you are asking. For my design each component knows its entity parent and each entity has a HasComponent type function for querying on a specific component type which returns either a shared pointer to the component or an empty (NULL) shared pointer. The entities store their components in a map or hash map type structure for quick lookup. To get component specific information I am forced to cast the returned pointer to the type I requested and I basically implemented a checked_cast type function which will do the more costly dynamic_cast in debug mode and just a static_cast in release mode.

Currently for game events I have an event dispatcher so entities can interact together and with the overall game world. The decision I made for now, which might get changed later, is to have each component within the entity register callbacks with the event dispatcher for the events it should handle for their parent entity. Again the same problem arises, a specific event object is created and passed through the callback function pointer to the component and the component function must cast through the checked_cast implemented earlier to the specific event it was expecting to extract the information it needs to do its task.

As a side note I used the boost::function and boost::bind for the callback implementation in the event dispatcher and just the usual register/unregister for messages. I have been following the fast delegates thread here and have looked over the library and it looks like a nice solution that I might try to integrate into my existing code.

This is my initial coding effort with a few minor refactorings and I am still not sure if this is a good design. It is certainly not fully featured and I have already experienced some initial aches and pains especially when event handling requires information from a lot of components of multiple entities. I think one of the most important design decisions for a successful component-based entity is choosing a good set of components so they are not too coupled.

I am very interested in this topic and would love to hear how others' feel about it and are dealing with the problems they run into.

#3 Sneftel   Senior Moderators   -  Reputation: 1776

Like
0Likes
Like

Posted 10 September 2007 - 03:13 AM

Bump. Other points of view?

[Edited by - Sneftel on September 10, 2007 10:13:06 AM]

#4 vtchill   Members   -  Reputation: 180

Like
0Likes
Like

Posted 10 September 2007 - 06:30 AM

A lot quieter then I was expecting for this thread. I was hoping to see a lot of comments and arguments for/against this type of design. Also would like to hear some people's experiences that are further along in their implementation than I am.

#5 dmatter   Crossbones+   -  Reputation: 3002

Like
0Likes
Like

Posted 10 September 2007 - 06:54 AM

Quote:
Original post by vtchill
A lot quieter then I was expecting for this thread. I was hoping to see a lot of comments and arguments for/against this type of design. Also would like to hear some people's experiences that are further along in their implementation than I am.

I think it could be a busier thread. I for one am struggling to get my head round it, maybe another/more thorough explanation is needed to get people going?

Perhaps its me, I seem to be having a bad day reading code [dead], but I'm having trouble just understanding the very top of Sneftel's snippet:
Quote:
class FooComponent
{
SomeComponent(shared_ptr<Entity> entity);
...
};

I can't decide what that is, or meant to be, I'm verging on sayings it looks like a typo so FooComponent should read SomeComponent, and then that makes it a constructor?

#6 vtchill   Members   -  Reputation: 180

Like
0Likes
Like

Posted 10 September 2007 - 07:08 AM

well maybe a quick overview to the ideas behind the component-based system would be helpful. Here are a couple of links explaining how others envision it. Maybe these will get the creative juices flowing.

This first link is a simple walk-through of a component-based hierarchy showing some of the advantages it could potentially have.
link 1

This link is just a broad overview comparing the traditional deep entity hierarchy against a component based one.
link 2

#7 Sneftel   Senior Moderators   -  Reputation: 1776

Like
0Likes
Like

Posted 10 September 2007 - 07:12 AM

Quote:
Original post by dmatter
Perhaps its me, I seem to be having a bad day reading code [dead], but I'm having trouble just understanding the very top of Sneftel's snippet:
Quote:
class FooComponent
{
SomeComponent(shared_ptr<Entity> entity);
...
};

I can't decide what that is, or meant to be, I'm verging on sayings it looks like a typo so FooComponent should read SomeComponent, and then that makes it a constructor?

Whoops. Fixed.


#8 Palidine   Members   -  Reputation: 1275

Like
0Likes
Like

Posted 10 September 2007 - 07:23 AM

The system I'm trying to design:

Essentially an entity is a container for components whose responsibility is to route events between components. The idea is that a component is a chunk of logic with a set of output events and a series of input events to which it responds.

Components then don't care explicitly about other components, they just use the more generic events which other components create and pass across the Entity bus. You could, therefore, throw any component into any entity and gain some/all of it's functionality (depending on what other components are also present).

One downside here, of course, is the extra overhead of event passing and catching. But I think the upside, is modularity and (potentially) threadability. w/r/t the latter, right now I have each entity guaranteed to have it's components all processed in the same thread, but going forward I may toy around with using components as the logical unit of threading. [The bigger idea of the project is a job-based multi-threaded, multi-core design]

The other downside has been actually trying to code the Components. I'm so used to the paradigm of m_pathfinder->GetNextSubGoal(), that trying to abstracting that information flow through event messaging has been difficult to orchestrate. Some of those problems have resulted from poor breakdown of tasks into components, but some (i hope) is just the challenge of a paradigm shift.

-me

#9 dmatter   Crossbones+   -  Reputation: 3002

Like
0Likes
Like

Posted 10 September 2007 - 07:24 AM

@vtchill:
Thanks for the links, I should have been clearer, I do understand what a component based system is when I said about finding it hard to understand I was referring to Sneftel's specific implementation.

@Sneftel:
Ok, that makes things easier (although I had assumed it was a mistake) [smile].

Is the hash-map really a hash-multi-map?
I ask because with your initial component based model an entity could be composed out of many components, with your new arrangement it's not clear how an entity can be composed from more than one.

Quote:
The thing I like most about this is that there's no need for the components to swim back to their subsystem. In fact, they generally don't need to know about the subsystem at all. More subtly, in the case where one component type (BarComponent, say) depends on another (FooComponent), the BarSubsystem can listen to the FooSubsystem for add/remove events instead of the World, automatically matched up to the component it cares about.

What mechanism do subsystems use to listen in on each other?

What could be really helpful is to see in comparison a deeper explanation of how this new system fixes the two major problems you identified with the first model.


Edit: *Smack forehead* - It's just clicked how an entity is composed from multiple components [lol]

#10 Sneftel   Senior Moderators   -  Reputation: 1776

Like
0Likes
Like

Posted 10 September 2007 - 07:40 AM

Quote:
Original post by dmatter
@Sneftel:
Ok, that makes things easier (although I had assumed it was a mistake) [smile].

Is the hash-map really a hash-multi-map?
I ask because with your initial component based model an entity could be composed out of many components, with your new arrangement it's not clear how an entity can be composed from more than one.

No, it's really a hashmap, because although a given Entity may be associated with many Component-like things, it's generally associated with only one FooComponent. For BarComponents, you'd have a BarSubsystem to hold those for each Entity. (I should note that in my actual codebase none of these things are called "subsystems"; it's just a name which makes sense here.)

Quote:
What mechanism do subsystems use to listen in on each other?

It varies by the exact relationship. One example: My pathfinding subsystem makes frequent nearest-neighbor or all-in-range queries to the spatial culling subsystem, specifying a point in space and getting back a list of entities near that point. The PathfindingSubsystem knows about the SpatialCullingSubsystem, but it doesn't know about SpatialCullingComponents. In other circumstances the opposite is true; a PhysicsComponent might know about the AnimationComponent to feed in blended dynamics, but wouldn't know about the AnimationSubsystem. Those take care of the first bullet point I mentioned.

Quote:
What could be really helpful is to see in comparison a deeper explanation of how this new system fixes the two major problems you identified with the first model.


Quote:
What could be really helpful is to see in comparison a deeper explanation of how this new system fixes the two major problems you identified with the first model.

The spatial culling system is a good example. You can think of a SpatialCullingComponent as an Entity's "assigned representative" for the spatial culling subsystem. When the entity is added to the world, rather than some factory system giving it the component, the SpatialCullingSubsystem says "hmm, a new Entity. I'll have to assign it a representative." That representative is then responsible for reporting positional changes back to the subsystem so that the quadtree can be changed. The Entity has no idea that anything is happening, other than a few listeners being added to its events. Since the Subsystem, not the factory, is in charge of the Component's creation, the Component knows about both the Entity and (if necessary) the Subsystem at the moment of creation, and the Subsystem knows about the Component. That takes care of the second bullet point.

As a side note, notice that there's no base Component class. I suppose there's some common functionality that could be factored out to a superclass, but the different types of components are never used interchangeably. The same is true of Subsystem.

#11 vtchill   Members   -  Reputation: 180

Like
0Likes
Like

Posted 10 September 2007 - 07:45 AM

Quote:
Original post by Palidine
The system I'm trying to design:

Essentially an entity is a container for components whose responsibility is to route events between components. The idea is that a component is a chunk of logic with a set of output events and a series of input events to which it responds.

Components then don't care explicitly about other components, they just use the more generic events which other components create and pass across the Entity bus. You could, therefore, throw any component into any entity and gain some/all of it's functionality (depending on what other components are also present).

One downside here, of course, is the extra overhead of event passing and catching. But I think the upside, is modularity and (potentially) threadability. w/r/t the latter, right now I have each entity guaranteed to have it's components all processed in the same thread, but going forward I may toy around with using components as the logical unit of threading. [The bigger idea of the project is a job-based multi-threaded, multi-core design]

The other downside has been actually trying to code the Components. I'm so used to the paradigm of m_pathfinder->GetNextSubGoal(), that trying to abstracting that information flow through event messaging has been difficult to orchestrate. Some of those problems have resulted from poor breakdown of tasks into components, but some (i hope) is just the challenge of a paradigm shift.

-me


This sounds very similar to the architecture I am trying to create and it sounds like you are a little further along with your implementation. I like the idea of the event passing scheme and I think it offers a lot of advantages. I also have had some problems with breaking tasks into components correctly and it really makes things much harder if you don't choose them wisely. So far do you like this paradigm better than the traditional?

#12 pancakedice   Members   -  Reputation: 159

Like
0Likes
Like

Posted 10 September 2007 - 08:28 AM

Quote:
Original post by vtchill
Quote:
Original post by Palidine
The system I'm trying to design:

Essentially an entity is a container for components whose responsibility is to route events between components. The idea is that a component is a chunk of logic with a set of output events and a series of input events to which it responds.

Components then don't care explicitly about other components, they just use the more generic events which other components create and pass across the Entity bus. You could, therefore, throw any component into any entity and gain some/all of it's functionality (depending on what other components are also present).

One downside here, of course, is the extra overhead of event passing and catching. But I think the upside, is modularity and (potentially) threadability. w/r/t the latter, right now I have each entity guaranteed to have it's components all processed in the same thread, but going forward I may toy around with using components as the logical unit of threading. [The bigger idea of the project is a job-based multi-threaded, multi-core design]

The other downside has been actually trying to code the Components. I'm so used to the paradigm of m_pathfinder->GetNextSubGoal(), that trying to abstracting that information flow through event messaging has been difficult to orchestrate. Some of those problems have resulted from poor breakdown of tasks into components, but some (i hope) is just the challenge of a paradigm shift.

-me


This sounds very similar to the architecture I am trying to create and it sounds like you are a little further along with your implementation. I like the idea of the event passing scheme and I think it offers a lot of advantages. I also have had some problems with breaking tasks into components correctly and it really makes things much harder if you don't choose them wisely. So far do you like this paradigm better than the traditional?


In fear of polluting the thread I'd ask this question: What does an event look like in such a scenario? A brief code example would be greatly appreciated.

I'm still at the point trying to design a way for my objects to communicate, and the events part is just driving me crazy at some points.


Also, at Sneftels example, would the SomeComponent::Create(entity) be a static method within a "Foo"Component?



#13 NotYourAverageUser   Members   -  Reputation: 488

Like
0Likes
Like

Posted 10 September 2007 - 08:31 AM

I am currently in the process of designing my engine. Currently it is tending towards a more traditional component-based entity design as you describe in the top of your first post.

I am relatively new game engine design (a fact that dmatter could probably attest to) so any comments that I make are to expand my understanding and not really suggestions.

@Sneftel: What would be the problem with having your entity contain a map<component_type, shared_ptr<Component> >? Polling for a particular component type is now a simple lookup without waiting for a successful dynamic_cast.

If two distinct entities need to interact, one can ask the other if he has a component_type and the other checks his map and returns the requested component if it is contained.

If an entity needs two or more of it's internal components to interact it could simply ask itself for that component_type and check his own map.

I am a curious beast, and this is the approach that I was leaning towards. I have not even started to code, I am still in the design process.
Please excuse my naivety.

---------------------------------------------------------------
I have been aided in my quests to advance my knowledge and design skills either explicitly (by creating a topic) or implicity (by searching the forum) by all of you. I gracefully would like to openly thank (not just a ++rating) both Sneftel and dmatter for all of the help that may or may not even know that they offered to me directly.

#14 Sneftel   Senior Moderators   -  Reputation: 1776

Like
0Likes
Like

Posted 10 September 2007 - 08:54 AM

Quote:
Original post by DeafManNoEars
@Sneftel: What would be the problem with having your entity contain a map<component_type, shared_ptr<Component> >? Polling for a particular component type is now a simple lookup without waiting for a successful dynamic_cast.
Not much of a problem at all. Of the two issues with the standard component-based architecture, this is the less important one. An intersting complicating factor, however, arises if/when you start using inheritance with your components. Suppose I want SpatialCullingComponent to have a StaticSpatialCullingComponent subclass which promises to move very infrequently, so that it can be placed in a special kD-tree for faster lookups. Should the component_type of StaticSpatialCullingComponent be the same as that of SpatialCullingComponent, allowing lookups by the base type but forcing an explicit test for the derived type? Should it be different, forcing a manual search for all the different subclasses it might be every time the base class is wanted? Should it be entered under both types (using a shared pointer type, of course), raising the danger of visiting a single component multiple times during an iteration? With the outboard design, this factor doesn't exist, since there's no need for component_type.

Quote:
I have been aided in my quests to advance my knowledge and design skills either explicitly (by creating a topic) or implicity (by searching the forum) by all of you. I gracefully would like to openly thank (not just a ++rating) both Sneftel and dmatter for all of the help that may or may not even know that they offered to me directly.

Glad to be of service. IMHO, this is an area of game development which deserves more frequent discussion.

#15 Sneftel   Senior Moderators   -  Reputation: 1776

Like
0Likes
Like

Posted 10 September 2007 - 08:58 AM

Quote:
Original post by dingojohn
Also, at Sneftels example, would the SomeComponent::Create(entity) be a static method within a "Foo"Component?

Yeah, sorry. I probably should have just shown raw pointers, to simplify things. It's very much a tangent, but most of my classes tend to look like this (disregarding a bit of macro-based evilness):


class MyClass : public boost::noncopyable
{
public:
static shared_ptr<MyClass> Create(int a, float b)
{
return shared_ptr<MyClass>(new MyClass(a, b));
}

private:
MyClass(int a, float b)
{
...
}
};



This way, MyClass objects can always be assured that they have been created into a shared_ptr. The Create() method also gets around a primary limitation of boost::shared_from_this, when that becomes an issue.

#16 dmatter   Crossbones+   -  Reputation: 3002

Like
0Likes
Like

Posted 10 September 2007 - 09:32 AM

Quote:
Original post by Sneftel
No, it's really a hashmap, because although a given Entity may be associated with many Component-like things, it's generally associated with only one FooComponent. For BarComponents, you'd have a BarSubsystem to hold those for each Entity. (I should note that in my actual codebase none of these things are called "subsystems"; it's just a name which makes sense here.)

Yep, sorry the question was an oversight on my part; I clocked that this was your intention just before you posted (I refere you to the edit on my post above).

Quote:
Quote:
What mechanism do subsystems use to listen in on each other?

It varies by the exact relationship. One example: My pathfinding subsystem makes frequent nearest-neighbor or all-in-range queries to the spatial culling subsystem, specifying a point in space and getting back a list of entities near that point. The PathfindingSubsystem knows about the SpatialCullingSubsystem, but it doesn't know about SpatialCullingComponents.

And then it can use that entity list to find the components it's responsible for.. I like that.

Quote:
In other circumstances the opposite is true; a PhysicsComponent might know about the AnimationComponent to feed in blended dynamics, but wouldn't know about the AnimationSubsystem. Those take care of the first bullet point I mentioned.

How does the PhysicsComponent talk to the AnimationComponent?
So does this always mean that if an entity has a PhysicsComponent is must also have an AnimationComponent, what if it didn't?

It ought to be a suitably loose coupling between the two as it's not unreasonable to want physics attributes but without actually being animated.

Palidine mentions using events to communicate between components this is basically the ultimate in keeping the PhysicsComponent loosely coupled with the AnimationComponent. I'm not sure it really needs to be quite *that* loose but certainly a C++ reference is too restrictive.

Quote:
When the entity is added to the world, rather than some factory system giving it the component, the SpatialCullingSubsystem says "hmm, a new Entity. I'll have to assign it a representative."

Presumably there is still a factory of some description for informing (or firing off the adequate events to) each relevant subsystem that an entity requires a representitive for?

Quote:
The Entity has no idea that anything is happening, other than a few listeners being added to its events. Since the Subsystem, not the factory, is in charge of the Component's creation, the Component knows about both the Entity and (if necessary) the Subsystem at the moment of creation, and the Subsystem knows about the Component. That takes care of the second bullet point.

So out of interest, what's actually in the entity class now?

Quote:
As a side note, notice that there's no base Component class. I suppose there's some common functionality that could be factored out to a superclass, but the different types of components are never used interchangeably. The same is true of Subsystem.

I'm not convinced a superclass is really needed with with approach, particularly for the components unless you find there is actually a suitably factorable amount of functionality in common. As you say, they're not used interchangeably and polymorphism isn't required.

#17 Sneftel   Senior Moderators   -  Reputation: 1776

Like
0Likes
Like

Posted 10 September 2007 - 09:46 AM

Quote:
Original post by dmatter
Quote:
In other circumstances the opposite is true; a PhysicsComponent might know about the AnimationComponent to feed in blended dynamics, but wouldn't know about the AnimationSubsystem. Those take care of the first bullet point I mentioned.
How does the PhysicsComponent talk to the AnimationComponent?
So does this always mean that if an entity has a PhysicsComponent is must also have an AnimationComponent, what if it didn't?
Remember that things can be set up such that the creation of a PhysicsComponent is the result of the creation of an AnimationComponent, not the creation of an Entity. So the AnimationComponent is available to PhysicsComponent at creation time, and Entities without AnimationComponents aren't visible to the PhysicsComponent. (Of course, if you wanted the PhysicsComponent to be able to attach to entities without AnimationComponents, you'd listen to World instead of AnimationSubsystem, and on Entity addition simply ask AnimationSubsystem if it had a component for that Entity.)
Quote:
Palidine mentions using events to communicate between components this is basically the ultimate in keeping the PhysicsComponent loosely coupled with the AnimationComponent. I'm not sure it really needs to be quite *that* loose but certainly a C++ reference is too restrictive.
Personally, I have my doubts about the "blizzard of events" approach. I don't believe that it necessarily promotes loose coupling; on the contrary, I think it can promote strong coupling, by obfuscating the relationships between components.
Quote:
Presumably there is still a factory of some description for informing (or firing off the adequate events to) each relevant subsystem that an entity requires a representitive for?
Sort of. That happens when the entity is added to the World, not when it is created; so the creation of Components is separated from the creation of Entities. The World contains an event for Entity adding, which each subsystem hooks to.

Quote:
So out of interest, what's actually in the entity class now?
Positional information; also health, speed, that sort of thing. My intention with this design isn't to totally skeletonize Entity, but to encapsulate most areas of logic. I could make even position information into a Component; but since nearly all subsystems care about position information, this would simply have the effect of making them all depend on a PositionComponent instead of Entity, with no real benefit.

Quote:
Quote:
As a side note, notice that there's no base Component class. I suppose there's some common functionality that could be factored out to a superclass, but the different types of components are never used interchangeably. The same is true of Subsystem.
I'm not convinced a superclass is really needed with with approach, particularly for the components unless you find there is actually a suitably factorable amount of functionality in common. As you say, they're not used interchangeably and polymorphism isn't required.
I'm of the same opinion, mostly. One thing I've considered, though, is making Subsystem (renamed, probably) into a superclass, since there's a decent amount of bookkeeping code there.

#18 vtchill   Members   -  Reputation: 180

Like
0Likes
Like

Posted 10 September 2007 - 10:09 AM

@Sneftel
I really like your ideas so far with the subsystems. I have a couple of questions which are really just for me to get a better understanding and visual of how things work together.

How do entities handle game events such as a bullet hitting an entity? Do entities register up front which components to route the game events to or do the subsystems take care of intercepting these messages and routing them correctly?

Is the changing of component configuration for entities during gameplay handled by the subsystems or is it handled somewhere else?

What is stored at the top level of the game (events, list of entities, list of subsystems, etc.)?



#19 LoneDwarf   Members   -  Reputation: 227

Like
0Likes
Like

Posted 10 September 2007 - 11:11 AM

I have also gone down this dev road and I am always interested in how others are doing it. I am writing a MUD (like one or two other people are). My system is something like this.


//==================================================================================================
//==================================================================================================
class Sim_Com
{
virtual void setValue( const STL_String& name, const Prop_Value& value ) = 0;
virtual Prop_Value getValue( const STL_String& name ) = 0;

virtual void onConstruct()
virtual bool onMessage( Sim_IMessage* pMessage );
};

//==================================================================================================
//==================================================================================================
class Sim_Object
{
//----------------------------------------------------------------------------------------------
ID getID() const

//----------------------------------------------------------------------------------------------
Sim_Com* getCom( const STL_String& name ) const;
Sim_Com* getComType( const STL_String& typeName );
void* getCom( const X_Rtti* rtti ) const;
};



So my system has an object that has a list of components (com) and each com has a list of properties (prop). I don't allow for nested coms or props. Also I don't allow for more than one com of the same type.

I am into the game logic so I have had some experience with it past your typical graphical demo.
Some of the things I am liking so far are:

  • What I like the most about the system is that it allowed me to use XML to build all the objects and use a variant to populate the data at runtime.

  • I always have an access path using get/set Prop_Value. So I don't need any extra code to access coms through XML, lua and the game editor. So this is a lot of glue that got written once.

  • There is just one format of config file to worry about. In past shipped titles, team members tend to have their own config file formats for 'their' system.

  • It's really a nice feeling when you manage to create a new object type from existing coms.


  • Some of the things I hate are:

  • You have no really idea what an object is since it's a collection of coms.

  • Coms can get tightly coupled to other coms, which kind of breaks the whole point of the system.

  • Originally I had 5-10 virtual functions for stuff like onRender, onAdvanceTime, onPostAdvanceTime, etc but it was eatting up to much CPU just updating coms that might only support one. So I switch to a sigslot type system and now I have traded CPU for memory.

  • I decided not to allow additional coms to be added to an object once constructed. Managing coupling to coms was becoming a pain without this rule.

  • Order of updates and/or messages can be a problem also. Basiclly writing solid coms can be hard.

  • Things that have no business being in the object/com system tend to find their way in. When you have a hammer, everything starts looking like a nail...



  • #20 Sneftel   Senior Moderators   -  Reputation: 1776

    Like
    0Likes
    Like

    Posted 10 September 2007 - 11:15 AM

    Quote:
    Original post by vtchill
    How do entities handle game events such as a bullet hitting an entity? Do entities register up front which components to route the game events to or do the subsystems take care of intercepting these messages and routing them correctly?

    The entities aren't aware that a component system exists. Instead, they have events which the components can connect to. So an entity might have a HitByBullet event, and any components which cared about that would connect to HitByBullet when they were created.

    Quote:
    Is the changing of component configuration for entities during gameplay handled by the subsystems or is it handled somewhere else?
    I can't think of a situation where I'd add/remove components for an entity during gameplay, but if so, it would be handled by the subsystem.
    Quote:
    What is stored at the top level of the game (events, list of entities, list of subsystems, etc.)?
    In my current design, the Game (in addition to holding a list of entities) also holds some Subsystems. The Graphics subsystem is separate, though, because it's not part of the Game.




    Old topic!
    Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



    PARTNERS