Monday, July 24, 2017

c++ - Component-based design: handling objects interaction


I'm not sure how exactly objects do things to other objects in a component based design.


Say I have an Obj class. I do:


Obj obj;
obj.add(new Position());
obj.add(new Physics());

How could I then have another object not only move the ball but have those physics applied. I'm not looking for implementation details but rather abstractly how objects communicate. In an entity based design, you might just have:



obj1.emitForceOn(obj2,5.0,0.0,0.0);

Any article or explanation to get a better grasp on a component driven design and how to do basic things would be really helpful.



Answer



That is usually done using messages. You can find lots of details in other questions on this site, like here or there.


To answer your specific example, a way to go is to define a small Message class that your objects can process, e.g:


struct Message
{
Message(const Objt& sender, const std::string& msg)
: m_sender(&sender)

, m_msg(msg) {}
const Obj* m_sender;
std::string m_msg;
};

void Obj::Process(const Message& msg)
{
for (int i=0; i {
// let components do some stuff with msg

m_components[i].Process(msg);
}
}

This way you're not "polluting" you Obj class interface with component-related methods. Some components can choose to process the message, some might just ignore it.


You can start by calling this method directly from another object:


Message msg(obj1, "EmitForce(5.0,0.0,0.0)");
obj2.ProcessMessage(msg);

In this case, obj2's Physics will pick the message, and do whatever processing it needs to do. When done, it will either:




  • Send a "SetPosition" message to self, that the Position component will pick;

  • Or directly access the Position component for modifications (quite wrong for a pure component-based design, as you can't assume every object has a Position component, but the Position component could be a requirement of Physics).


It's generally a good idea to delay the actual processing of the message to the next component's update. Processing it immediately could mean sending messages to other components of other objects, so sending just one message could quickly mean an inextricable spaghetti stack.


You'll probably have to go for a more advanced system later on on: asynchronous message queues, sending messages to group of objects, per-component registering/unregistering from messages etc.


The Message class can be a generic container for a simple string as shown above, but processing strings at runtime isn't really efficient. You can go for a container of generic values: strings, integers, floats... With a name or better yet, an ID, to distinguish different types of messages. Or you can also derive a base class to fit specific needs. In your case, you could imagine an EmitForceMessage that derives from Message and adds the desired force vector -but beware of the runtime cost of RTTI if you do so.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...