   yet another pure component game model topic, getting brother components.
  I'm trying to implement a "pure component based model". The basic idea is to avoid the "Game Object" class and to have objects defined by components.http://cowboyprogramming.com/2007/01/05/evolve-your-heirachyNow, I understand perfectly the whole thing but I'm not satisfied on how I'm getting each of the components associated with some id. For example, say I have std::maps for my graphic components and physic components. Do I have to search each map independently when "getting" each component if I already know the object's id? Isn't there a better approach? maybe I could tangle some pointers, but how?[Edited by - toglia on May 24, 2010 9:45:03 PM]
  Yes, you would have to query/lookup each component in its collection. In the "object as pure aggregation" implementation there is no need for any sort of an entity class whatsoever. The idea of an entity is represented by a single id that identifies all the components belonging to that entity, so if you had a PhysicsComponent and a HealthComponent belonging to the entity with id 100, they'd each be stored in their respective table or map with id 100.I find the object as pure aggregation implementation to be more simple to understand when compared to a relational database. Each of your component types is represented by a table having a foreign key to the Entities table. The Entities table consists only of one field: entity_id. The author does say though that this looking up of each component every use became impractical for them. They compromised by allowing components to cache references to other components belonging to the entity.   ;Quote:Original post by smrYes, you would have to query/lookup each component in its collection. In the "object as pure aggregation" implementation an entity is nothing but a collection of distinct components. There is one ID that identifies all the components belonging to that entity, so if you had a PhysicsComponent and a HealthComponent belonging to the entity with id 100, they'd each be stored in their respective table or map with id 100.The object as pure aggregation implementation is really more simple to understand when I compare it to a relational database. Each of your component types is represented by a table having a foreign key to the Entities table. The Entities table consists only of one field: entity_id.Well, yeah! I'm up for it that way.Quote:Original post by smrThe author does say though that this looking up of each component every use became impractical for them. They compromised by allowing components to cache references to other components belonging to the entity.This was my initial question, how would you implement these references? a pointer to each other component? maybe another map?   ;  I think you can sum up the two approaches like:typedef unsigned int GameObject;class System{  std::map<GameObject,Component*> m_Components;};andclass GameObject{  std::vector<Component*> m_Components;};Quote:Original post by togliaQuote:Original post by smrThe author does say though that this looking up of each component every use became impractical for them. They compromised by allowing components to cache references to other components belonging to the entity.This was my initial question, how would you implement these references? a pointer to each other component? maybe another map?Pointers would be the simplest way, assuming the lifetime of all components belonging to an object are the same. If individual components can be removed from an object, then some kind of weak-pointer type would be useful instead.class Component_B{  Component_A* m_Sibling;  virtual void OnGameObjectCreated( int parentObjectID )  {    m_Sibling = System_A.GetComponent( parentObjectID );  }};;  The point where I get lost in the whole entity/component system is when you look at the system as if you're looking at a relational database, how do you vary behaviors for components of the same type? You could derive components and override methods, but none of that behavior is evident when you look at the system from the data side. To the database a HealthComponent is nothing more than a record in a table having three columns: COMPONENT_ID, ENTITY_ID, VALUE. The lazy answer is to "put behaviors in a ScriptComponent," but I think that's just a workaround. Another way is to keep some sort of an identifier in the HealthComponent table that identifies the "type" of HealthComponent. A HealthComponent factory could use that column to determine the specific flavor of HealthComponent needed for the entity, but I think this is another workaround and breaks encapsulation.Anyone have a good solution to this? Am I overlooking something obvious? Or is this just one of those situations where it's best just to do something practical even though it goes against what you consider to be right?   ;Quote:Original post by togliaThis was my initial question, how would you implement these references? a pointer to each other component? maybe another map?I think he just used a private field in the Component itself. No reason to make it more complicated than it should be.class MyComponent {    private PhysicsComponent physicsComponent = null;    ...    function update(){   if (physicsComponent == null || physicsComponent.entityId != this.entityId) {  // Cache the new component  physicsComponent = PhysicsSystem.getComponent(this.entityId);   }    }};Quote:Original post by smrThe point where I get lost in the whole entity/component system is when you look at the system as if you're looking at a relational database, how do you vary behaviors for components of the same type?The RDBMS analogy only really works for the data layouts, it doesn't say anything about how to deal with any behaviours (polymorphic or not).If they do different things, then they're probably different components. If they supply some kind of polymorphic/interchangable behaviour, you can have a table of MagicHealthComponents, a table of GiantCrabHealthComponents, etc, but have the clients of these components deal with them through an IHealthComponent interface.I've used systems where as well as being able to say "get me a component *of this type* for this game object" you could also say "get me any component *that implements this interface* for this game object"   ;  Yup, both implementations seem pretty reasonable. Thanks.   ;  Another thing. As I have read in many places the pure aggregation aspect of the model makes it possible for objects to mutate easily, this is because adding and destroying components can be done in runtime. Ok, so from what I understand the component will be responsible for all the allocation and cleansing of the memory it uses, right?But generally 3rd party libraries are restrictive on how you do this, in fact you can't delete a mesh instance in ogre by yourself, you have to do it with ogre's interface, and that also true for other stuff like physics engines.Therefore, components are almost forced to use pointers and not references like I was imagining in order to be more "cache-friendly".Did I miss something?   ;Quote:Original post by togliathe component will be responsible for all the allocation and cleansing of the memory it uses, right? ... 3rd party libraries are restrictive on how you do this ... you have to do it with [their] interfaceYeah. If you had an "ogre mesh component", it would use their API to allocate/release the mesh.Quote:Therefore, components are almost forced to use pointers and not references like I was imagining in order to be more "cache-friendly".Do you mean pointers instead of values? You can use references like this (but it won't change the "cache-friendliness" of it)ThirdPartyMesh* AllocMesh( const string& name ) { return new ThirdPartyMesh(name); }void ReleaseMesh( ThirdPartyMesh* p ) { delete p; }class MeshComponent{  ThirdPartyMesh& data;  MeshComponent( const string& name ) : data(*AllocMesh(name)) {}  ~MeshComponent() { ReleaseMesh(&data); }};If you're using a 3rd party library like Ogre, then you're bound to the ways that it chooses to store and access it's data, and it's their responsibility to make their design "cache friendly". If Ogre is jumping all over RAM, there's not much you can do about from the outside.   