Sign in to follow this  
simotix

Components and Attribute linking

Recommended Posts

This is a common thing that happens with components, however, I am looking for an efficient way to handle it.

In a nutshell, here is what I mean. I have an Entity, that will contain two components (for keeping it simple), which are RectangleRenderingComponent and AABBRigidBodyComponent.

Now, when the attribute of "Height" changes on RectangleRenderingComponent, it should somehow communicate to AABBRigidBodyComponent that the Height of the RectangleRenderingComponent changed, so the AABB's Height should change also. I am not sure how to allow this type of communication or "listening". Does anyone have any suggestions?

Share this post


Link to post
Share on other sites
This is similar to how I handle components and properties on entities.

What I do, is that I have the entity own all properties. Components just store references directly to those properties, so you don't duplicate data. Second, the property class contains a sigslot implementation, which allows you to register listeners when the data change. So the operators and set function on the property class that change data, fires the ValueChanged signal, so that all listeners are called. The listeners are function pointers, so it's very efficient. This is a great way to decouple component dependencies where components work on the same functionality (which actually usually is because they need to operate on the same data...).

http://code.google.com/p/propcompentitysystem/source/browse/trunk/include/Property.h

I use ClanLib to handle the signal/slot functionality, but you can easily exchange that for something else, like the sigslo API for example:
http://sourceforge.net/projects/sigslot/

To add a property and add a listener to that property, all I do is:

SomeComponent::SomeComponent(const Entity &entity) : entity(entity)
{
this->some_property = entity.AddProperty<some_type>("SomeProperty");
this->somePropChangedSlot = this->some_property.ValueChanged().connect(this, &SomeComponent::OnSomePropChanged);
}

void SomeComponent::OnSomePropChanged(const some_type &oldValue, const some_type &newVValue)
{
//.. do something about it.
}





Share this post


Link to post
Share on other sites
Quote:
Original post by Trefall
What I do, is that I have the entity own all properties.


This is interesting ... where did you learn about this technique? From what I have seen, the components typically own their own properties.

Quote:
Original post by Trefall
The listeners are function pointers, so it's very efficient.


How is this done? Are you using some sort of delegate?

Share this post


Link to post
Share on other sites
I also noticed that in this thread that you suggest a different method for something I am also trying to achieve? I was wondering why is that, and what is the difference?

Share this post


Link to post
Share on other sites
Quote:
Original post by simotix
Quote:
Original post by Trefall
What I do, is that I have the entity own all properties.


This is interesting ... where did you learn about this technique? From what I have seen, the components typically own their own properties.


This is a method I came up with on my own. I'm not saying I was the first to put this approach to use, but I didn't pick the approach off the net. The reason why I came up with it, was because I noticed that the usual reason why components would get coupled, was because they needed access to the same data, not the same functionality. So this was my solution to the problem, as sending excessive events between components to get around the problem just got to an unmanageable point after a while. This method isn't perfect however. A centralized data container makes multithreading slightly more complicated and difficult for instance... but that's only a problem if you need multithreading of course. Also, I haven't really payed any heed to memory fragmentation yet, which especially on consoles is very important. Other approaches, like those that apply a system where each component type sits in it's own list under it's own component type manager, might easier come around the problem of fragmentation, but it shouldn't be too hard to implement in my approach either, using freelists (see Game Programming Gems 4 & 5) for instance.

Quote:
Original post by simotix
Quote:
Original post by Trefall
The listeners are function pointers, so it's very efficient.


How is this done? Are you using some sort of delegate?


Yeah, it's using a sig/slot system, ala what Qt uses for instance. So the signal holds on to a list of slots. A slot holds a function pointer and the instance of the object that owns the function pointer. So that when a signal is invoked, all the slots (function pointers) gets called. I use the sigslot system in ClanLib. It works something like this:

CL_Slot slot = property.ValueChanged().connect(this, &ThisClass::OnPropertyChanged);




Once this slot has been registered with the property's value changed signal, next time the value of the property change, the ThisClass' OnPropertyChanged function will be called.

Quote:
Original post by simotix
I also noticed that in this thread that you suggest a different method for something I am also trying to achieve? I was wondering why is that, and what is the difference?


The approach I suggested in that thread was directly related to the problem-domain that the OP wanted to solve. Using observers like that isn't very general, as you'd have to write a lot of classes to solve relatively simple problems. A more general approach, as presented in my component system using properties, solves the same problems, but in a much more general form.

You could say that my properties' sigslot system is a form of observer pattern like presented in that topic, but it's approach is much more flexible. The event system would come in addition, as sometimes you'll have to send direct messages of what to do, not change properties to get logic flow going in components. The way I've done this is a simple forwarding mechanic, where the Entity has a sendEvent method, that just push the event forward to all it's components. This could be further optimized by letting components register themselves to specific events, that the entity knows which events to delegate to which components, though unless you start to script components in Lua for example, this isn't critical for performance.

Share this post


Link to post
Share on other sites
If height is part of entity then entity should own it and pass it to the relevant components that it consumes as needed. Why a rendering component would be changing height of the entity I don’t know. Two of the key points of components are that they should encapsulate functionality and be interchangeable.

RectangleRenderingComponent and AABBRigidBodyComponent should only communicate if one is used by the other in which case it should just push the correct values down when it used. Otherwise there should be no communication

This ensure that if you have two entities car and ball and one method move, You can simply specify rigidbody as the movement component for car and flexiblebody as the movement component for ball as part of your config then each entity will handle its movement correctly at runtime. They both components have to implement the same interface, but then you can also add as many additional movement types as you want by creating additional components that implement that interface.





Share this post


Link to post
Share on other sites
Quote:
Original post by Trefall
Yeah, it's using a sig/slot system, ala what Qt uses for instance. So the signal holds on to a list of slots. A slot holds a function pointer and the instance of the object that owns the function pointer. So that when a signal is invoked, all the slots (function pointers) gets called. I use the sigslot system in ClanLib. It works something like this:


I am probably going to switch my code base to have the entity contain the properties, rather then the component. This seems like it can be a great help with communication with the only real downfall being memory fragmentation, which it seems can be fixed by using some sort of factory for component creation.

I am curious as to why you use a "PropertyData" class, and not just merge that into Property?

Also, if anyone has any other suggestions I would love to hear them. Not that I don't like your method Treefall (I do like it), just the more ideas the better.

Share this post


Link to post
Share on other sites
Quote:
Original post by simotix
I am curious as to why you use a "PropertyData" class, and not just merge that into Property?

Also, if anyone has any other suggestions I would love to hear them. Not that I don't like your method Treefall (I do like it), just the more ideas the better.


The PropertyData and Property class follows the Pimpl pattern. For the way we treat properties in multiple components, this is very convenient, because you can do:

Property<float> a;
Property<float> b = a;

and those two will share the same PropertyData.

And no worries asking around for other approaches! You should take all approaches into account, and then form your own approach that feels best for you.

Share this post


Link to post
Share on other sites
Where did your definition for "CL_Signal_v2" come from? While looking at the signal/slot code from sourceforge, I don't see that, I end up seeing signal#.

Share this post


Link to post
Share on other sites
Quote:
Original post by simotix
This is interesting ... where did you learn about this technique? From what I have seen, the components typically own their own properties.

How is this done? Are you using some sort of delegate?


I would prefer the components own their own properties.
And if more than one components need to share all or most properties, extract the properties to a single component which is a property-only component (same like a data only struct in C++).
Then the component won't depend on entity. They only depends on the property component. Indeed in my mind entity is a virtual thing that doesn't really own anything except a set of components.
And if we need to change the underlying data, we only need to replace the property components and corresponding control components. Think that if we want to use 3D data to replace 2D.

For callback(delegate), libsigc++ is for it, Boost function is also for it. But I use my own one.

Sorry I'm not the one you quoted...

Share this post


Link to post
Share on other sites
CL_Signal_v2 comes from ClanLib.

That's a cool approach to the problem too wqking. I see that we think of the entity differently, as I like to think of the entity as the physical result of the components and properties it's made out of: Behavior and Data, because that allows me to sit at my desk and start picking apart items around me into components and properties, and my brain just works better that way.

Doing it your way definitely gets around some major problems I've experienced myself when battling components owning properties, like the problem you addressed by moving shared properties out into a third property-only component. My only beef with that approach, though, is that now a component requires another component to exist for itself to exist... How do you deal with this coupling problem robustly?

Share this post


Link to post
Share on other sites
Here is my replies in another thread, mainly around coupling and depending.
http://www.gamedev.net/community/forums/topic.asp?topic_id=588290&whichpage=1
Just noticed your guys are also in that thread...

Plus that thread, here is my more opinion.

It's fine that one component requires another known component exists.
A render component can't live without a position component.
As I said in the another thread, the render can retain the type (an integer ID) of the position component so when render is updating, it gets a pointer to position component.
If it gets a NULL pointer, an failed assert should occur, or just does nothing.

If we put the position information to entity, that will solve the "require existing" problem, but it has some more serious problems,
1, Entity contains redundant data. An event trigger entity doesn't need position (ignore the one that triggered by position...) and it doesn't need to be rendered too.
2, If we have both 3D and 2D entities, then entity has too contain 3D position, again, redundant to 2D entity.
3, Hard to extend the position data. How about if we need to add a new position system which x, y, z works completely different with the existing one? Add to entity again? Then the entity will bloat as if there is no component system.

And if we move the data/properties to component, then we have answer to above questions,
1, Event trigger entity will not have either position nor render component. No redundant data.
2, Just add a new 3D position component (may inherited from 2D one).
3, Add a new position component, then add new "control" component to control it.

No means that my approach is good or bad (nothing is absolutely good or bad), but that's what I thought and I did in my tiny component/entity framework.
And this topic is quite interesting, hope we can discuss more.

Share this post


Link to post
Share on other sites
Yeah, it's a very interesting topic. Especially in that there are so many different approaches to solving it, that it's interesting to look at pros/cons of different approaches.

From your description I see that we meet the same requirements in the end. My approach ends up with no redundant data either, because it's the components that adds properties to the entity. So if you have a renderer component that only needs 2d positions, it'll just push a

position = entity->addProperty<Vec2f>("Position", Vec2f(0.0f, 0.0f));




Using the pimpl pattern, the component will hold on to a direct reference to the PropertyData, so there's no lookup overhead for components to access the property data either, they just set up their required properties with the entity at construction. The entity knows which properties has already been added, so if another component tries to add the same property, it'll just get the reference to the already existing property in return. So no redundant data in my approach either, it's just a slightly different way of solving the same problem.

A slight difference I see here, that might pro my approach over yours, is that my approach has been strictly designed to expect components to share properties. So, if I have a component that requires the Position property, then down the line, maybe 3 months later, a different programmer comes on the team and adds another component that holds the same property, then he don't have to care how many times that property has been used before, or know about which components has already used it, because my system is designed this way, while in your system, the programmer would have to know about the existence of the property in a different component to prevent unwanted data duplication and pull it out in a third component... that might lead to some really hard-to-find bugs, where you'd have two components working on their own version of the same property, but without throwing any errors... so it would just result in strange behavior... or do you have a book-keeping system on top of your components that prevents this from happening?

Share this post


Link to post
Share on other sites
OK, I see there is no redundant data, that would be great.

For the question in your last paragraph, here is my opinion,
Properties can be split to two category,
Category 1, private data. It's only used by one component. This kind of data doesn't need to be single component or published in the entity. No others care about it except the component using it.
Category 2, public and shared data. More than one components need to access the same data.
The data is in a single component or published in the entity (your approach).
For this kind data, there must be a convention to define how to use it. You can't use X for z-buffer, etc.
So in your example, in my implementation, if any new developer needs the position, he needs to find the component named "position" and learn the convention (xyz or xy, float or int, etc). And because he want to use the position semantic, he must following the convention instead of invent his own position. Of course if there is "position" yet, he will be the first guy to invent it.
And he doesn't need to care about which and how other components use "position", no, he doesn't care, nor his new component, because all persons and components are working under the same convention. What he needs to do is just respect the convention.

It's the same in your method. If a guy needs to use or add "position", he must follow the semantic of "position" and understand it's the entity's xyz, not the texture's xyz, etc.

BTW, seems we had hijacked this thread and is a little off topic. :-)

Share this post


Link to post
Share on other sites
Yeah, let's leave it at that for the sake of the OP :)

And I agree with your semantic argument, that's a perfectly valid argument. But just to mention how that would work in my component system:

If one component already added an entity->AddComponent<Vec3f>("Position"), and then a new component was added by a second, clueless programmer that did an entity->AddComponent<Vec3i>("Position"), then that would throw an exception in my engine (float vs integer), if those two components were added to the same entity, since there are two different types of Position added to the same entity.

So the new programmer who implemented the second component with the Vec3i position wouldn't have to know of the component already implementing the Vec3f position, because the engine would notify him about this the moment he tried to use it in an entity already holding a conflicting type of position.

Position is a property of very obvious multi-component interest of course, but with more subtle property types, it might not be obvious from the first time it's used in a component that it will be shared with another component in 3 months, when everyone has forgot about it's existence in the first place...

I'm just saying that this issue could lead to some strange behavior that would be hard to track, if there's no safetyguard around such a thing. Then, of course, one could make the argument that every programmer should be aware of the properties held by different components when a new component is introduced, as unaware definition of new components can potentially lead to a truck-load of problems in itself. So in the end, the whole argument isn't really more than a minor detail :P

Edit: Ouch, I guess I didn't leave it at that after all. Sorry!

Share this post


Link to post
Share on other sites
Quote:
Original post by wqking
BTW, seems we had hijacked this thread and is a little off topic. :-)


Quote:
Original post by Trefall
Yeah, let's leave it at that for the sake of the OP :)


That is fine, it may have gone a little off topic, but this is still good knowledge.

Quote:
Original post by Trefall

If one component already added an entity->AddComponent<Vec3f>("Position"), and then a new component was added by a second, clueless programmer that did an entity->AddComponent<Vec3i>("Position"), then that would throw an exception in my engine (float vs integer), if those two components were added to the same entity, since there are two different types of Position added to the same entity.


You could check to make sure that a property with the same name has not been added.

Share this post


Link to post
Share on other sites
Quote:
Original post by simotix
You could check to make sure that a property with the same name has not been added.


Yeah, this is what I do. First I check whether a property of same name has been added to entity, but then I also check whether the properties of same name is also of same type. Using template function for adding the property, you can't return a property of a different type than you ask for without breaking it to garbage anyway, through static_cast. Right now I use a dynamic_cast on this to check for NULL exception (different type), but this could quite easily be exchanged for an enum type list (would require that you know all possible property variable types) or that you only perform dynamic cast in debug mode.

Share this post


Link to post
Share on other sites
Trefall, you can write your own casting macro.
In debug mode, it uses dynamic_cast, and in release mode, it uses static_cast, so no runtime performance overhead.

One question, how do you organize your properties in the entity? A map? That maybe performance issue because there are so many entities and properties created/destroyed at runtime.
Any better solution?
If your target platform is high end PC, that may be not a problem, but I hope my code can run on the mobile such as iPhone, Nokia which is powerful enough but far from PC.

Share this post


Link to post
Share on other sites
Quote:
Original post by wqking
Trefall, you can write your own casting macro.
In debug mode, it uses dynamic_cast, and in release mode, it uses static_cast, so no runtime performance overhead.


Yes for sure, but do I really want to do this? At some production level, I'll definitely want to work on scripts while in release mode so the program is optimized when I work at that level... I might just go in and add a static_cast towards the very last part of a game's testing phase or something... or add a third runtime mode or something. Thing is, I'm a bit hesitant to turn it into static_cast during production, because it wouldn't be able to catch any type conflicts, and I'm pretty sure designers and artists would want to run in release-mode, and not deal with super-slow std containers in debug! Properties usually hold some common types of data though... so simply enumerating those types, then create a Type class with a static type checker that overloads to all expected types, should do it in a very optimal fashion, even for release :)

Quote:
Original post by wqking
One question, how do you organize your properties in the entity? A map? That maybe performance issue because there are so many entities and properties created/destroyed at runtime.
Any better solution?
If your target platform is high end PC, that may be not a problem, but I hope my code can run on the mobile such as iPhone, Nokia which is powerful enough but far from PC.


Right now I'm indeed using a simple map. I haven't looked at optimizing the insertion time of this yet, because my runtime performance is very good, since my components hold direct reference to the properties, and don't look up in the map every time they need to access them. But you are definitely right here though, if I was to target my system towards mobiles, I'd have to do a ton more optimization on the system. I just haven't come to that point yet, where my profiling has told me that I need to, which, like you pointed out, definitely has to do with the platform I've focused on thus far :)

I'd be interested to hear how you do this though, but let's not get further off topic in this thread. Would you just make a new thread in your reply, please?

Share this post


Link to post
Share on other sites
The dynamic/static cast trick is only for omit the dynamic cast (which can be omitted, i.e, the type won't really be unknown or wrong at runtime) overhead. It was to answer your "Right now I use a dynamic_cast on this to check for NULL exception (different type)"
So if you can always use static cast, or jthe overhead of dynamic_cast is not a problem, that's fine.

For the last question, I have some answer in my another thread (I gave the link before), here is a very short summary, so no new thread, :-)
1, I use integer ID instead of string name at runtime. So even I use map, it's much faster than string.
2, I use the ID as the index of an array. So the time complexity is always O(1) to get a component.
3, If array memory is a problem, I can use some sparse array for it (0..N-1 is in one array, N..2N-1 is in second array, so if no N..2N-1, I don't need to allocate it).

Even if performance is not an issue to you yet, it will not harm if you use integer ID internally instead of string name. Just a suggestion.

Share this post


Link to post
Share on other sites
Quote:
Original post by wqking
The dynamic/static cast trick is only for omit the dynamic cast (which can be omitted, i.e, the type won't really be unknown or wrong at runtime) overhead. It was to answer your "Right now I use a dynamic_cast on this to check for NULL exception (different type)"
So if you can always use static cast, or jthe overhead of dynamic_cast is not a problem, that's fine.

For the last question, I have some answer in my another thread (I gave the link before), here is a very short summary, so no new thread, :-)
1, I use integer ID instead of string name at runtime. So even I use map, it's much faster than string.
2, I use the ID as the index of an array. So the time complexity is always O(1) to get a component.
3, If array memory is a problem, I can use some sparse array for it (0..N-1 is in one array, N..2N-1 is in second array, so if no N..2N-1, I don't need to allocate it).

Even if performance is not an issue to you yet, it will not harm if you use integer ID internally instead of string name. Just a suggestion.


The only problem I have with changing to static_cast, is that I won't be able to handle the exception gracefully. The application would just crash hard as it tried to access garbage. While dynamic_cast actually returns NULL if the types don't match, so I can handle it with a good exception message to the user. But yeah, we understand each other here I'm sure :)

Right, integer IDs is a good optimization over strings, but can also be quite difficult to work with from a programmer's perspective. Like, if you choose to use an enum list, you have to update this enum every time you add a new component type, which results in a deep recompile of the engine. Also, using enums might become a bit difficult if you want to mix C++ defined components and script defined components (if you start to script components in eg. Lua). Strings here works more streamlined and conveniently, but with an extra overhead on performance of course. So it's a trade of development convenience vs performance. One could always go through the engine and change to enums at the end of the project, before public release is made, or add in some compiler #if's to handle this maybe... I'm sure there are other smart ways to optimize this as well... but I haven't really looked into it yet, as it hasn't really been an issue for me thus far. Strings are just very convenient, and you can in fact do quite a few compares each frame without it tolling performance too badly (on PC that is). Integers are definitely a huge optimization compared to strings though, I'm not neglecting that argument, I've just chosen to trade that for development convenience thus far.

Share this post


Link to post
Share on other sites
One easy way, as recommended by Jason Gregory and used by myself, is to use the hash produced by a string as its id. Then you just have to maintain an associated table. How you want to maintain that table is up to you. I maintain a global table outside of my program using a local SQL database for small projects, and then run a pre-process on my code before it is compiled to replace any instances of strings with their id. The pre-process also checks to make sure that there is no collision, but with the murmur hash I have never had a problem with that on all of my projects.

Of course you could simplify the process a great deal, and even modify your approach. The approach above is just what works best for myself.

Share this post


Link to post
Share on other sites
That's a very interesting technique, and I definitely have had some form of hashing in mind for optimizing my component system, though especially use of hash maps. Looking up based on the hash of strings sounds very both fast and convenient! I love it when you can get the both of two worlds! :D Of course, you will run the risk of conflicts, as strings do take up more bits than say 32 bit integer hash... but sounds like you haven't had problems with this as of yet :) I saw the link you gave supports up to 128 bit hash with good performance! Sounds promising :)

Share this post


Link to post
Share on other sites
Quote:
Original post by Halifax2
One easy way, as recommended by Jason Gregory and used by myself, is to use the hash produced by a string as its id.


Where is this detailed? I do have a copy of Game Engine Architecture, but I don't remember seeing this in it.

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