Component Based Objects?
- oddrose
- Orc
- Posts: 470
- Joined: Thu Feb 15, 2007 2:08 pm
- Location: Gothenburg, Sweden
- Contact:
- KungFooMasta
- OGRE Contributor
- Posts: 2087
- Joined: Thu Mar 03, 2005 7:11 am
- Location: WA, USA
- x 16
- Contact:
It means you can create your objects in real time if you wanted to, by adding and removing components. Your object design/functionality is not determined at compile time, but based off the components it has. It's more editor and script friendly, promotes designing GameObjects piece by piece, and allows for any combination of components.
Creator of QuickGUI!
- oddrose
- Orc
- Posts: 470
- Joined: Thu Feb 15, 2007 2:08 pm
- Location: Gothenburg, Sweden
- Contact:
- TheN00B
- Gnoblar
- Posts: 24
- Joined: Fri Aug 31, 2007 5:41 am
- Location: Portland, OR, USA
- Contact:
Ran into this thread, and thought I'd share my thoughts. I originally read about this type of object management in the Game Programming Gems 5 text, although I don't remember the author or the chapter title off the top of my head. I have employed an object management system similar to what you are all speaking of, so I thought I'd share. My system is fairly static, and I'm know of several areas that could be improved upon.
In this system the only thing that identifies an object is an ID (which is a hash value). There is no actual object class, struct or otherwise as I had difficulty separating object purpose from component purpose. Each component inherits from an IComponent virtual class, as well as an interface. The interface declares the functionality of the component type (i.e. Physical component would have an add force method), and the IComponent virtual class declares the functionality of a component (such as creation, serialization etc...).
Interfaces are identified by an interface ID, and components are identified by a component ID. In my current implementation these are enum values, but this approach wouldn't be too hard to change.
All my components are "stored" in a database, which in my implementation is a struct. This database comprises of three peices of data and looks like the following:
The first peice of information is an array of the struct SComponentTypeInfo which contains a function pointer to the creation method, a function pointer to the destruction method, and a hash value for the component (for identification). The struct looks like this:
The second piece of information is a set of components that are of a certain interface type (an array of sets).
The last piece of information is another array that matches the interface type to a map keyed by the object ID (which is a hash) that contains a component.
The components I implemented all read data from an XML file, which I realize isn't the best solution, but at the moment is the easiest. I can then create an object editor, or even a world editor, that can change objects by adding and removing components, or changing the properties of a component.
My system also has iterators that aid in controlling the data, as well as an object manager class that aids in querying the database, and controlling components. The biggest thing missing at this point is a messaging system, but I am working on an event manager that is separate from the object system.
I am certainly a noob (I mean look at my name ), but I thought I'd share my method of managing components.
Cheers!
P.S. Please do criticize, I certainly could use it
In this system the only thing that identifies an object is an ID (which is a hash value). There is no actual object class, struct or otherwise as I had difficulty separating object purpose from component purpose. Each component inherits from an IComponent virtual class, as well as an interface. The interface declares the functionality of the component type (i.e. Physical component would have an add force method), and the IComponent virtual class declares the functionality of a component (such as creation, serialization etc...).
Interfaces are identified by an interface ID, and components are identified by a component ID. In my current implementation these are enum values, but this approach wouldn't be too hard to change.
All my components are "stored" in a database, which in my implementation is a struct. This database comprises of three peices of data and looks like the following:
Code: Select all
struct SObjectManagerDB
{
//////////////////////////////////////////////////////////////////
// Static component type data
SComponentTypeInfo mComponentTypeInfo[NUM_COMPONENT_TYPE_IDS];
std::set<EComponentTypeID> mInterfaceTypeToComponentTypes[NUM_INTERFACE_IDS];
//////////////////////////////////////////////////////////////////
// Dynamic component data
std::map<CObjectID, IComponent*> mComponentTypeToComponentMap[NUM_COMPONENT_TYPE_IDS];
};
Code: Select all
struct SComponentTypeInfo
{
ComponentCreationMethod mCreationMethod;
ComponentDestructionMethod mDestructionMethod;
CHash mTypeHash;
};
The last piece of information is another array that matches the interface type to a map keyed by the object ID (which is a hash) that contains a component.
The components I implemented all read data from an XML file, which I realize isn't the best solution, but at the moment is the easiest. I can then create an object editor, or even a world editor, that can change objects by adding and removing components, or changing the properties of a component.
My system also has iterators that aid in controlling the data, as well as an object manager class that aids in querying the database, and controlling components. The biggest thing missing at this point is a messaging system, but I am working on an event manager that is separate from the object system.
I am certainly a noob (I mean look at my name ), but I thought I'd share my method of managing components.
Cheers!
P.S. Please do criticize, I certainly could use it
There is a message in my alphabits, it says Ooooooooooooooo...
- steven
- Gnoll
- Posts: 657
- Joined: Mon Feb 28, 2005 1:53 pm
- Location: Australia - Canberra (ex - Switzerland - Geneva)
- Contact:
@TheN00B: Interesting solution.
One point bothers me: if you want to access the Physical component of an object you need to search in all the map and use RTTI to identify if a physical component exist. Isn't this expensive?
Also how do you add/remove objects? Are you obliged to lock the map (with a read-write mutex)? Or did you achieve to separate the creation/destruction phase from the read phase so that you don't need a lock?
One point bothers me: if you want to access the Physical component of an object you need to search in all the map and use RTTI to identify if a physical component exist. Isn't this expensive?
Also how do you add/remove objects? Are you obliged to lock the map (with a read-write mutex)? Or did you achieve to separate the creation/destruction phase from the read phase so that you don't need a lock?
- TheN00B
- Gnoblar
- Posts: 24
- Joined: Fri Aug 31, 2007 5:41 am
- Location: Portland, OR, USA
- Contact:
@steven: You are correct that searching through the map is an expensive process, which makes it a less then suitable data structure. However for my implementation objects are controlled by reacting to events, so thus far it hasn't been an issue.
I am not saying that using those data structures is a great solution, just something I have temporarily created.
Creation, destruction, and component access is controlled by the object manager class, which employs a read-write mutex on the database. My game currently has three separate threads, one which is controlled by CEGUI, so really only two threads that access this system. Thus far I have had no problems with data corruption.
Thanks very much for the thoughts, I guess other then component communication, access time is a major flaw of this system. I may have to re-think some things to correct this issue.
I am not saying that using those data structures is a great solution, just something I have temporarily created.
Creation, destruction, and component access is controlled by the object manager class, which employs a read-write mutex on the database. My game currently has three separate threads, one which is controlled by CEGUI, so really only two threads that access this system. Thus far I have had no problems with data corruption.
Thanks very much for the thoughts, I guess other then component communication, access time is a major flaw of this system. I may have to re-think some things to correct this issue.
There is a message in my alphabits, it says Ooooooooooooooo...
-
- Halfling
- Posts: 57
- Joined: Wed Jul 14, 2004 10:12 am
- Location: Berlin
Component based systems help with the inheritance problem, especially when the "design" needs to be tweak-able. Moreover - as said - you can compose/prototype new entities in the run-time.
Something I am heading for it to have lists for certain types of components. Thus the physics manager/service/server has a list of all 'active' physics objects - adding objects or removing objects will alter that list. This removes the need for the search.
Unfortunately not all ogre classes have a user data member that can be used as bi-directional link i.e. for 'tagging' materials or physics objects - so you need to have additional look-up tables for that you can ask "Has this entity a physics tag?"
Something I am heading for it to have lists for certain types of components. Thus the physics manager/service/server has a list of all 'active' physics objects - adding objects or removing objects will alter that list. This removes the need for the search.
Unfortunately not all ogre classes have a user data member that can be used as bi-directional link i.e. for 'tagging' materials or physics objects - so you need to have additional look-up tables for that you can ask "Has this entity a physics tag?"
- eugen
- OGRE Expert User
- Posts: 1422
- Joined: Sat May 22, 2004 5:28 am
- Location: Bucharest
- x 8
- Contact:
- volca
- Gnome
- Posts: 393
- Joined: Thu Dec 08, 2005 9:57 pm
- x 1
- Contact:
I agree with you, this can be implemented with the component(property) listeners.Dom wrote:Component based systems help with the inheritance problem, especially when the "design" needs to be tweak-able. Moreover - as said - you can compose/prototype new entities in the run-time.
Something I am heading for it to have lists for certain types of components. Thus the physics manager/service/server has a list of all 'active' physics objects - adding objects or removing objects will alter that list. This removes the need for the search.
Unfortunately not all ogre classes have a user data member that can be used as bi-directional link i.e. for 'tagging' materials or physics objects - so you need to have additional look-up tables for that you can ask "Has this entity a physics tag?"
I think you should not need the reverse lookup (under certain conditions that is).
You'd only need it if you used scene queries or such. If the entities are only slaves to your code, and you do all the engine related tasks yourself (including collision detection, etc), then the data member should not be needed at all.
The archetype approach should help with the rapid prototyping (pseudocode):
Code: Select all
archetype physical {
property physics {
collision_type bounce
}
}
archetype ambient_sound_source {
property sound {
ambient_sound 'default.wav'
mode loop
}
}
object bug : physical, ambient_sound_source {
property position {
position 13.0, 14.5, 0.2
heading 90
pitch 75
bank 23
}
property render {
model bug.mesh
}
property sound {
ambient_sound 'bugsound.wav' // override the default setting
}
}
From the programmers view, the properties would have a defined set of fields, and should be able to self describe themselves. (renderPropDef.getFieldList(), etc.). Because the listeners to properties handle the actual view, the renderPropDef is an instance of the same class as for example soundPropDef, etc.
- Falagard
- OGRE Retired Moderator
- Posts: 2060
- Joined: Thu Feb 26, 2004 12:11 am
- Location: Toronto, Canada
- x 3
- Contact:
First let's be sure we're not hitting semantic problems here. I didn't read about your object/component design but it's entirely possible that your object is the same as my component and your component is the same as my behavior. If so, there's no difference - just the class names are different.steven wrote:@falagard Your object/component/behavior design seems to function like our object/component one.
But I fail to see the benefit of separating the behavior from the components.
Why not making specific components each with its own behavior?
Does it not simply add an additional indirection?
I don't have an object, then a component, then a behavior. I have components and behaviors.
-
- Halfling
- Posts: 57
- Joined: Wed Jul 14, 2004 10:12 am
- Location: Berlin
eugen, roger - i'll consider that.
volca, I agree that callbacks/listeners would do that. I like the possibility for the reverse look-ups as it helped me in the past to connect distinct systems without intense inter-webbing while allowing flexible scripting.
You say "only slaves to your code" ... I think this means that you have to wrap all the Ogre functionality? I.e. a component for position (wrapping an ogre entity), a component for rendering (wrapping mesh) ?
Falagard,
Indeed one need to pay attention what people understand under certain words - for example volca's 'properties' could be my 'components'
Another example is: manager/service/server/system
volca, I agree that callbacks/listeners would do that. I like the possibility for the reverse look-ups as it helped me in the past to connect distinct systems without intense inter-webbing while allowing flexible scripting.
You say "only slaves to your code" ... I think this means that you have to wrap all the Ogre functionality? I.e. a component for position (wrapping an ogre entity), a component for rendering (wrapping mesh) ?
Falagard,
Indeed one need to pay attention what people understand under certain words - for example volca's 'properties' could be my 'components'
Another example is: manager/service/server/system
- volca
- Gnome
- Posts: 393
- Joined: Thu Dec 08, 2005 9:57 pm
- x 1
- Contact:
Well, the property(component) does not wrap any class itself. It is a source of data for the given service which needs it/realizes it. That means f.ex. RenderModel property (if such existed) having one field string - modelName:Dom wrote: volca, I agree that callbacks/listeners would do that. I like the possibility for the reverse look-ups as it helped me in the past to connect distinct systems without intense inter-webbing while allowing flexible scripting.
You say "only slaves to your code" ... I think this means that you have to wrap all the Ogre functionality? I.e. a component for position (wrapping an ogre entity), a component for rendering (wrapping mesh) ?
The RenderService listens to all the messages from propertyGroup that holds the properties of this kind, and for any change, it adds/removes/changes the entity attached to the sceneNode.
The sceneNode is updated with messages from Position property.
Etc.
The PhysicsService, if it was written, would listen to the Physics properties and to position. It will use different classes to implement the physics, and will be the source of contact events and such.
There is one thing probably important to mention: There is only a little place to implement object behavior directly using the properties, that would be way too inflexible. So every object has script list, and these scripts listen to the events happening in the game and react to them (to be designed).
Please note that the target of my effort is not to build a new engine, but to mimic an existing one (DarkEngine).
- anomalous_underdog
- Kobold
- Posts: 30
- Joined: Tue Aug 21, 2007 8:20 pm
here's a powerpoint presentation made by gas games about component based game objects in their game dungeon siege 1
http://www.drizzle.com/~scottb/gdc/game-objects.ppt
http://www.drizzle.com/~scottb/gdc/game-objects.ppt
-
- Halfling
- Posts: 57
- Joined: Wed Jul 14, 2004 10:12 am
- Location: Berlin
volca, this is very interesting, thank you for the info.
Let me think a bit aloud: a Ogre-Service plugin could publish new property types to the system - i.e. position and geometry. This would be counterparts to Ogre's entity and mesh. If a property gets modified, a callback or message would add the property to the "modified/toUpdate" list of the manager. The Ogre manager also would 'publish' tasks i.e. for updating ogre objects. I am thinking of tasks as those (or a single one) may be easier distributed across multiple cores with less locks than one would maybe need if the callback modifies the object directly. A task would then "execute" the new property values onto the engine (that is wrapped by the service - in this case OGRE) counterparts.
I have no idea if it removes flexibility and makes things too complicated, but it sounds very interesting and something I might want to tryout. On the other hand it still sounds like a lot of properties and services that one has to create and maintain.
Btw. I have something similar to your properties. I have parameters and they are composed of basic (predefined) data-fields. Several parameters can be combined into a parameter-set, which is maybe a equivalent of your 'property' concept. This way new types can easily be created using data and still be modified by a GUI-Editor.
Let me think a bit aloud: a Ogre-Service plugin could publish new property types to the system - i.e. position and geometry. This would be counterparts to Ogre's entity and mesh. If a property gets modified, a callback or message would add the property to the "modified/toUpdate" list of the manager. The Ogre manager also would 'publish' tasks i.e. for updating ogre objects. I am thinking of tasks as those (or a single one) may be easier distributed across multiple cores with less locks than one would maybe need if the callback modifies the object directly. A task would then "execute" the new property values onto the engine (that is wrapped by the service - in this case OGRE) counterparts.
I have no idea if it removes flexibility and makes things too complicated, but it sounds very interesting and something I might want to tryout. On the other hand it still sounds like a lot of properties and services that one has to create and maintain.
Btw. I have something similar to your properties. I have parameters and they are composed of basic (predefined) data-fields. Several parameters can be combined into a parameter-set, which is maybe a equivalent of your 'property' concept. This way new types can easily be created using data and still be modified by a GUI-Editor.
-
- Gnoblar
- Posts: 2
- Joined: Wed Oct 03, 2007 2:42 am
Have any of you looked at the ideas presented at this site http://www.devmaster.net/articles/oo-game-design/
Game objects that would hold a list of actions and states. Actions would perform events which would trigger movements, changes of that object's states, sounds, etc. States would hold things like the objects health, or mana, or whatever else would be state worthy.
These could be then plugged into an object a runtime. An action could then query another object it happens to have interacted with, to see if it has the specific action or state that that action is suppose to change.
I haven't had time to implement this, but I was curious what other people think about this idea? The rest of the system may be a bit much at the moment but i think the action/state system is interesting.
Game objects that would hold a list of actions and states. Actions would perform events which would trigger movements, changes of that object's states, sounds, etc. States would hold things like the objects health, or mana, or whatever else would be state worthy.
These could be then plugged into an object a runtime. An action could then query another object it happens to have interacted with, to see if it has the specific action or state that that action is suppose to change.
I haven't had time to implement this, but I was curious what other people think about this idea? The rest of the system may be a bit much at the moment but i think the action/state system is interesting.
- Zeal
- Ogre Magi
- Posts: 1260
- Joined: Mon Aug 07, 2006 6:16 am
- Location: Colorado Springs, CO USA
Just read that article. I liked how he emphasized the separation between 'rendering' and 'logic', but I didnt quite understand the need for so many different 'game objects' (entity, action, state, and space)..
Kind of interesting, but it didnt really relate to this topic (he didnt talk about object composition hardly at all).
Kind of interesting, but it didnt really relate to this topic (he didnt talk about object composition hardly at all).
- volca
- Gnome
- Posts: 393
- Joined: Thu Dec 08, 2005 9:57 pm
- x 1
- Contact:
This is how I planned to do the asynchronous communication as well. The property definitions are stored in files, as the 3 games that should be handled (Thief 1, Thief 2 and System Shock 2) contain some differences in the definitions.Dom wrote:volca, this is very interesting, thank you for the info.
Let me think a bit aloud: a Ogre-Service plugin could publish new property types to the system - i.e. position and geometry. This would be counterparts to Ogre's entity and mesh. If a property gets modified, a callback or message would add the property to the "modified/toUpdate" list of the manager. The Ogre manager also would 'publish' tasks i.e. for updating ogre objects. I am thinking of tasks as those (or a single one) may be easier distributed across multiple cores with less locks than one would maybe need if the callback modifies the object directly. A task would then "execute" the new property values onto the engine (that is wrapped by the service - in this case OGRE) counterparts.
I have no idea if it removes flexibility and makes things too complicated, but it sounds very interesting and something I might want to tryout. On the other hand it still sounds like a lot of properties and services that one has to create and maintain.
Btw. I have something similar to your properties. I have parameters and they are composed of basic (predefined) data-fields. Several parameters can be combined into a parameter-set, which is maybe a equivalent of your 'property' concept. This way new types can easily be created using data and still be modified by a GUI-Editor.
But actually, for now, I dropped the threading option, which can be seen as a wastage of a good and viable option, considering the separation. I spent some time thinking about that, and decided that if something can really be moved to a different thread, then it's the physics. I'll try to prepare the loop management code to be able to do this, as it should be possible to avoid the problems with threading as physics basically could prepare object position for the next frame, while the renderer is running the previous one. On the start of the next frame, the updated positions would be deployed synchronously.
It seems we are doing the things in a similar way. I see the self-description of the properties (or whatever they're called) as a very good thing. I use a kind of Variant implementation for the property fields, which even more improves the possibility to auto-generate the editor window for objects. No editor for now, but a replacement of DromED (ShockED) would surely be appreciated.
- Zeal
- Ogre Magi
- Posts: 1260
- Joined: Mon Aug 07, 2006 6:16 am
- Location: Colorado Springs, CO USA
So for a very basic example, could you do something like...
Start with a generic 'Component' base class. All your components (position, 3d model, all the 'things' that make up your game object...) will be sub classes of 'Component'. Then create a 'GameObject' class that has a list/vector of Component pointers. That way you could expand a GameObject to include pointers to any number of custom components (assuming the base Component class had all the interface functionality you needed, like a virtual 'update()' function, ect...).
Am I close? Or am I missing something..
Start with a generic 'Component' base class. All your components (position, 3d model, all the 'things' that make up your game object...) will be sub classes of 'Component'. Then create a 'GameObject' class that has a list/vector of Component pointers. That way you could expand a GameObject to include pointers to any number of custom components (assuming the base Component class had all the interface functionality you needed, like a virtual 'update()' function, ect...).
Am I close? Or am I missing something..
- KungFooMasta
- OGRE Contributor
- Posts: 2087
- Joined: Thu Mar 03, 2005 7:11 am
- Location: WA, USA
- x 16
- Contact:
Yep, you have the basic principle down.
Some components will also have the need to communicate with other components (if they exist!) to carry out all their functionality. How they communicate or interact isn't a clear solution, IMO. Seems to be a mix of messages and direct calling, depending on your situation.
Also the idea of GameObjects having child GameObjects sounds pretty cool, but its only an idea at this point. (for me at least)
Some components will also have the need to communicate with other components (if they exist!) to carry out all their functionality. How they communicate or interact isn't a clear solution, IMO. Seems to be a mix of messages and direct calling, depending on your situation.
Also the idea of GameObjects having child GameObjects sounds pretty cool, but its only an idea at this point. (for me at least)
Creator of QuickGUI!
- Zeal
- Ogre Magi
- Posts: 1260
- Joined: Mon Aug 07, 2006 6:16 am
- Location: Colorado Springs, CO USA
So to check to see if a GameObject has X component, you just number ALL your components (using a simple enum), and give the base Component class a 'getType()' method which would return this enum id? Then you could iterate through all Component* in your GameObject looking for a specific id (and when you find it, you know its position in the list). Right?need to communicate with other components (if they exist!)
- JohnJ
- OGRE Expert User
- Posts: 975
- Joined: Thu Aug 04, 2005 4:14 am
- Location: Santa Clara, California
- x 4
That's one way to do it. If I use a component system, I'm planning to retreive other components by a string name (managing IDs and enums really defeats the purpose of a modular system, I think). For speed, each component can "prefetch" the pointers of the other modules, like I posted on page 3.Zeal wrote:So to check to see if a GameObject has X component, you just number ALL your components (using a simple enum), and give the base Component class a 'getType()' method which would return this enum id? Then you could iterate through all Component* in your GameObject looking for a specific id (and when you find it, you know its position in the list). Right?
But I don't know how well this would work in a multithreaded or message based system.
- Wolfmanfx
- OGRE Team Member
- Posts: 1525
- Joined: Fri Feb 03, 2006 10:37 pm
- Location: Austria - Leoben
- x 99
- Contact:
- Zeal
- Ogre Magi
- Posts: 1260
- Joined: Mon Aug 07, 2006 6:16 am
- Location: Colorado Springs, CO USA
Hmm I dont quite get that. Correct me if im wrong, but the 'modular/scripting' side does NOT give you the power to create new Components. The Components themselves have to be first hard coded in C, THEN you can 'compose' custom objects via scripting. And by compose I simply mean - select form a list of pre defined Components and 'group' them together (so a 'Tree' could have position, visual, and 'talkable' component if you wanted to make a 'tree that could talk' GameObject).(managing IDs and enums really defeats the purpose of a modular system, I think).
So I dont see why managing a enum for your hard coded Components would be that big of a deal?
- JohnJ
- OGRE Expert User
- Posts: 975
- Joined: Thu Aug 04, 2005 4:14 am
- Location: Santa Clara, California
- x 4
It probably won't be a big deal, provided you have a certain set of components and you're sure that's not going to change. I just think that this system is so well suited for a plugin-able component architecture that it would be almost a waste not to make each Component a truly self-contained module (compiler-wise), especially when it can be done with no loss in performance.Zeal wrote:Hmm I dont quite get that. Correct me if im wrong, but the 'modular/scripting' side does NOT give you the power to create new Components. The Components themselves have to be first hard coded in C, THEN you can 'compose' custom objects via scripting. And by compose I simply mean - select form a list of pre defined Components and 'group' them together (so a 'Tree' could have position, visual, and 'talkable' component if you wanted to make a 'tree that could talk' GameObject).(managing IDs and enums really defeats the purpose of a modular system, I think).
So I dont see why managing a enum for your hard coded Components would be that big of a deal?
- KungFooMasta
- OGRE Contributor
- Posts: 2087
- Joined: Thu Mar 03, 2005 7:11 am
- Location: WA, USA
- x 16
- Contact:
I recently played around with factories (similar to plugins), and realized the only way to maintain a 'Type' property when allowing users to create their own Types on the fly was to use a string. It would be cool to allow appending to an enum, but that would probably be abused a lot.
Creator of QuickGUI!