Components and Attribute linking

Started by
25 comments, last by Triton 13 years, 4 months ago
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?
Advertisement
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.}
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?
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?
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.
I'm not sure there's a good efficient way to deal with it. So much so that I looked towards programming language research to make it suck less.

(not to derail the thread... sig/slot or some similar event mechanism will get things done).
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.





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.
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.
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#.

This topic is closed to new replies.

Advertisement