Jump to content

  • Log In with Google      Sign In   
  • Create Account


Component based system, doing it right?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
19 replies to this topic

#1 Moonkis   Members   -  Reputation: 273

Like
1Likes
Like

Posted 21 February 2013 - 05:40 PM

So I'm trying to make an more sophisticated component system, most have been not as large, but this time around I'm aiming at highly flexible one so this is my first attempt at making anything decent.

I'v grasped the basic aspects of Components, and the only thing that ever has bothered me about it is communications between all of the components, especially in "larger" ( not 3xA large but larger than Pong or Tetris ) "games".

Now I'v came up with an design that I feel comfortable with ( In some aspects anyways ), It's inspired from Don't Starves Lua-Component system as well as a topic ( it can be found here: http://gameprogrammingpatterns.com/component.html#when-to-use-it ).

Basically the Entity ( Container Object ) have an array/vector/map of components that is indexed by an enum-table called "ComponentID", which is where all the components are defined as integer ids.

This helps to sort each component based on it's "priority" which in turn makes sure they are called in the right order as well as clarifies in code what it refers to. 

Entity has under 'protected' two methods called "hasComponent(ComponentID id)" & getComponent(ComponentID id), the later is a template'd function that dynamically casts the right class depending of the ID.

The Component Interface has access to these via friend-keyword seeing as those functions should be exposed but not publicly. Here is the syntax I'm currently targeting within my components, this is from the component "Movable" - which handles movement and coordinates.

 





/* Inside the Movable.cpp */
if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Down ) )
{
	float ny = y + MOVEMENT_SPEED * dt;
}
if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Up ) )
{
	float ny = y - MOVEMENT_SPEED * dt;
}
if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Right ) )
{
	float nx = x + MOVEMENT_SPEED * dt;
}
if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Left ) )
{
	float nx = x - MOVEMENT_SPEED * dt;
}

/* If an entity does have an Physics Component. */
if ( entity.hasComponent( ComponentID::PHYSICS ) )
{
	/* Request it as such. */
	Physics* p = entity.getComponent<Physics*>( ComponentID::PHYSICS );
	/* Check if it's solid and there is no tile/terrain collision */
	if ( p->isSolid() && !p->terrainCollision( nx, ny, entity ) )
	{
		x = nx;
		y = ny;
	}
}
else /* Just move it as such. */
{
	x = nx;
	y = ny;
}

 

I know it's not exactly perfectly decoupled, but this is my first time around and I though it was an easy and flexible way of getting data from other components that said component needs, such as Graphic-Component needs coordinates where to draw the sprite, or in this case, if an Physics-Component exists then check if the object is solid and make sure it doesn't collide.

Now here is where you come in, I'm wondering if this is really a good, or acceptable way of handling communications/interactions between components?  Am I doing the component pattern completely wrong or am I on the right track?

Kind regards, Moonkis.

EDIT:
If anyone is wondering the function: "Entity::getComponent(ComponentID id)" will be using static_cast<> because I'v read that it performs WAY better than dynamic_cast<> and I am confident I'm trying to cast an correct type ( the map<ComponentID, IComponent*> looks like that ).
 


Edited by Moonkis, 21 February 2013 - 06:17 PM.


Sponsor:

#2 RobMaddison   Members   -  Reputation: 656

Like
1Likes
Like

Posted 22 February 2013 - 02:24 AM

I do my comp/ent system this way. My entity holds a map of components keyed on the component enum type, which means you don't have to loop through components to find the one you want, works really well for me. I guess if you wanted more than one similar-type components, you could hold a vector of components in the map, wouldn't take much to change

#3 NightCreature83   Crossbones+   -  Reputation: 2733

Like
0Likes
Like

Posted 22 February 2013 - 03:57 AM

You both are using an aggregation pattern for components only. The component system stores more then just components in the game object it also stores attributes, and it is these you should set which in turn the components can access. If you need inter component communication you should send a message to the currently connected entity and let it pass it done to the rest of it's components, if this component shouldn't handle it send it to all currently active game objects and let them handle it like the internal message passing works.

 

A component shouldn't need to know anything about any other component or be passed any real information other then the entity in it's update function, see this article and all its previous posts on it http://www.altdevblogaday.com/2011/09/23/the-game-entity-part-v-future-ponderings/

 

I use fast string hash functions in my engine to do the look up of components and attributes that are stored on the entity because name based indexing is easier to read whilst developing. And it also allows me to add components and attributes multiple times under different names.

 

If your entity contained a position attribute you could have fixed this like this:

 

void KeyboardInputComponent::Update(Entity& entity)
{
    Vector3& position = entity.getAttribute( Hash.HashString( "position" ) ).getValue<Vector3>(); //Or how you store your position
   if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Down ) )
   {
       float ny = y + MOVEMENT_SPEED * dt;
   }
   if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Up ) )
   {
       float ny = y - MOVEMENT_SPEED * dt;
   }
   if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Right ) )
   {
       float nx = x + MOVEMENT_SPEED * dt;
   }
   if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Left ) )
   {
       float nx = x - MOVEMENT_SPEED * dt;
   }
  
    position += Vector3(nx, ny,  0.0f);
}
 
void PhysicsComponent::Update(Entity& entity)
{
    Vector3& position = entity.getAttribute( Hash.HashString( "position" ) ).getValue<Vector3>(); //Or how you store your position
    //Do your collision here with the position you have updated before
}
 
//Or
void PhysicsComponent::respondToPositionChangedMessage(const PositionChangedMessage& msg)
{
   Vector3 position = msg.getPosition(); //Message is sent from the keyboard component
    //Do collision
}

Edited by NightCreature83, 22 February 2013 - 04:20 AM.

Worked on titles: CMR:DiRT2, DiRT 3, DiRT: Showdown, GRID 2, Mad Max

#4 EWClay   Members   -  Reputation: 659

Like
0Likes
Like

Posted 22 February 2013 - 04:20 AM

I minimise the use of getComponent by using a message system. I use it for communicating between the components of an entity as well as between entities. There's a message object for each type of message. The components register with the entity what kind of message they are interested in. Then it's just pDest->SendMessage(pFrom, message).

No need to care about which component, if any, is going to receive it. In fact, multiple components might pick up the message and do different things.

One thing - you don't get a return value. You might get a message back, later. For basic information I use tags on the entities, an idea I took from Unity.

The advantage is almost total decoupling of different component types. They don't even need to include each other's headers to communicate, just the messages they send or receive.

#5 Moonkis   Members   -  Reputation: 273

Like
0Likes
Like

Posted 22 February 2013 - 04:34 AM

I minimise the use of getComponent by using a message system. I use it for communicating between the components of an entity as well as between entities. There's a message object for each type of message. The components register with the entity what kind of message they are interested in. Then it's just pDest->SendMessage(pFrom, message).

No need to care about which component, if any, is going to receive it. In fact, multiple components might pick up the message and do different things.

One thing - you don't get a return value. You might get a message back, later. For basic information I use tags on the entities, an idea I took from Unity.

The advantage is almost total decoupling of different component types. They don't even need to include each other's headers to communicate, just the messages they send or receive.

This is what the linked topic also teaches ( sending a message ) problem is I'm having a hard time seeing it, there are so many types of messages that I don't know how to represent them, Objects? Integers? Strings? How would they look like? And more importantly how should they be parsed/interpreted? Does each component prase/interpret differently? Seems very un-efficient.
 

There's a message object for each type of message. The components register with the entity what kind of message they are interested in. Then it's just pDest->SendMessage(pFrom, message).

This part I can see...somewhat. How would that look like? I mean I'm assuming that "Message"-objects are derived from a base-class. Then I'm guessing that somewhere it needs to be casted to the appropriate message? How can the Components identify or Entity what type of message it is and how it should be casted?
 

You both are using an aggregation pattern for components only. The component system stores more then just components in the game object it also stores attributes, and it is these you should set which in turn the components can access. If you need inter component communication you should send a message to the currently connected entity and let it pass it done to the rest of it's components, if this component shouldnw't handle it send it to all currently active game objects and let them handle it like the internal message passing works.

 

A component shouldn't need to know anything about any other component or be passed any real information other then the entity in it's update function, see this article and all its previous posts on it http://www.altdevblogaday.com/2011/09/23/the-game-entity-part-v-future-ponderings/

 

I use fast string hash functions in my engine to do the look up of components and attributes that are stored on the entity because name based indexing is easier to read whilst developing. And it also allows me to add components and attributes multiple times under different names.

 

If your entity contained a position attribute you could have fixed this like this:

 

void KeyboardInputComponent::Update(Entity& entity)
{
    Vector3& position = entity.getAttribute( Hash.HashString( "position" ) ).getValue<Vector3>(); //Or how you store your position
   if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Down ) )
   {
       float ny = y + MOVEMENT_SPEED * dt;
   }
   if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Up ) )
   {
       float ny = y - MOVEMENT_SPEED * dt;
   }
   if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Right ) )
   {
       float nx = x + MOVEMENT_SPEED * dt;
   }
   if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Left ) )
   {
       float nx = x - MOVEMENT_SPEED * dt;
   }
  
    position += Vector3(nx, ny,  0.0f);
}
 
void PhysicsComponent::Update(Entity& entity)
{
    Vector3& position = entity.getAttribute( Hash.HashString( "position" ) ).getValue<Vector3>(); //Or how you store your position
    //Do your collision here with the position you have updated before
}
 
//Or
void PhysicsComponent::respondToPositionChangedMessage(const PositionChangedMessage& msg)
{
   Vector3 position = msg.getPosition(); //Message is sent from the keyboard component
    //Do collision
}

Components holds their variables they are using, Entity is basically just an shell where components lives, it doesn't own any attributes other than an array of components, rather is collected under the same roof. "Movable" haves the attributes float x,y nx, ny it's responsibilities is moving and keeping track of where it is.

Your approach is very interesting though, how does a component send a message? How does a component know what type of message it is? How does it reach the right components if they share they same base-class and lives under an anonymous array?


Edited by Moonkis, 22 February 2013 - 04:54 AM.


#6 EWClay   Members   -  Reputation: 659

Like
0Likes
Like

Posted 22 February 2013 - 04:57 AM

I don't parse messages, I send fixed message types, which are structs containing the message data as members. Each message type has a string identifier, typically just the name of the struct.

For example, a controller component might send a Move message, which contains a vector. The entiIty has a hash map of function pointers using the message identifier as the key. When the physics component gets the message, it is just as though a function had been called.

Overall, it's no more expensive than looking up a component directly and calling a function.

#7 Moonkis   Members   -  Reputation: 273

Like
0Likes
Like

Posted 22 February 2013 - 05:12 AM

I don't parse messages, I send fixed message types, which are structs containing the message data as members. Each message type has a string identifier, typically just the name of the struct.

For example, a controller component might send a Move message, which contains a vector. The entiIty has a hash map of function pointers using the message identifier as the key. When the physics component gets the message, it is just as though a function had been called.

Overall, it's no more expensive than looking up a component directly and calling a function.

Interesting so there is no base-struct? But don't you have to make a new send function EVERY time you want to implement a new message-type? There is a lot of information that is missing which confuses me a lot more instead of helping me understand.

I'm guessing the Entity has the send-function? Or it doesn't? Are there even an base class for components? How does it look like?

 

I just don't get it...what calls what and which holds what functions?


EDIT:


Maybe the send message is a template'd method? Something along the lines of:

 template<class T>
virtual void broadcast(T msg);

/* Calling it as */
broadcast<SoundMessage>(msg);


 
 


Edited by Moonkis, 22 February 2013 - 05:44 AM.


#8 Kylotan   Moderators   -  Reputation: 3338

Like
1Likes
Like

Posted 22 February 2013 - 05:54 AM

Moonkis: your system looks ok to me. Ignore anybody who tells you that "a component system should ...." because there is no 'True' way to do this. The way you are handling it is a lot like the way that Unity handles components - and good games are shipped with Unity on a regular basis.

 

I attempt to keep component-to-component references to a minimum, but I think that forbidding them entirely just serves to overcomplicate the system. Sometimes you really do want one component to be able to call a function on another component and get a response, and forcing that to go via message passing will create more brittle code than the alternative. I certainly don't think that's the goal you should aim for when you're still relatively inexperienced with component-based entities.

 

One small recommendation: don't do this:

if ( entity.hasComponent( ComponentID::PHYSICS ) )
Physics* p = entity.getComponent<Physics*>( ComponentID::PHYSICS ); 

Instead, do this:

 

Physics* p = entity.getComponent<Physics*>( ComponentID::PHYSICS );
if (p) { 

No point doing the lookup twice (once to check if it's there, once to get a pointer to it). Just return either the pointer or null.



#9 Moonkis   Members   -  Reputation: 273

Like
0Likes
Like

Posted 22 February 2013 - 06:02 AM

Moonkis: your system looks ok to me. Ignore anybody who tells you that "a component system should ...." because there is no 'True' way to do this. The way you are handling it is a lot like the way that Unity handles components - and good games are shipped with Unity on a regular basis.

 

I attempt to keep component-to-component references to a minimum, but I think that forbidding them entirely just serves to overcomplicate the system. Sometimes you really do want one component to be able to call a function on another component and get a response, and forcing that to go via message passing will create more brittle code than the alternative. I certainly don't think that's the goal you should aim for when you're still relatively inexperienced with component-based entities.

 

One small recommendation: don't do this:

if ( entity.hasComponent( ComponentID::PHYSICS ) )
Physics* p = entity.getComponent<Physics*>( ComponentID::PHYSICS ); 

Instead, do this:

 

Physics* p = entity.getComponent<Physics*>( ComponentID::PHYSICS );
if (p) { 

No point doing the lookup twice (once to check if it's there, once to get a pointer to it). Just return either the pointer or null.


Yes, sounds good to me! And I'll definitely change that.

Though for later/other projects I really want to get into this message systems but I'v been goggling like crazy and I haven't found any good example other than:

sendMessage(message);

But it still leaves me with a lot of questions like I'v asked in the posts above.



#10 EWClay   Members   -  Reputation: 659

Like
0Likes
Like

Posted 22 February 2013 - 06:47 AM

I don't parse messages, I send fixed message types, which are structs containing the message data as members. Each message type has a string identifier, typically just the name of the struct.

For example, a controller component might send a Move message, which contains a vector. The entiIty has a hash map of function pointers using the message identifier as the key. When the physics component gets the message, it is just as though a function had been called.

Overall, it's no more expensive than looking up a component directly and calling a function.

Interesting so there is no base-struct? But don't you have to make a new send function EVERY time you want to implement a new message-type? There is a lot of information that is missing which confuses me a lot more instead of helping me understand.

I'm guessing the Entity has the send-function? Or it doesn't? Are there even an base class for components? How does it look like?

 

I just don't get it...what calls what and which holds what functions?


EDIT:


Maybe the send message is a template'd method? Something along the lines of:

 template<class T>
virtual void broadcast(T msg);

/* Calling it as */
broadcast<SoundMessage>(msg);


 
 

 

Correct on all counts. And don't take anything I'm saying as being THE way to do it. There are many implementations and this is just mine.

 

SendMessage is a templated function in Entity. Messages are just structs. There's no base message type. There is a base component type, to allow Entity to hold base component pointers.

 

In practice it looks like this. I use macros to simplify some of the syntax.

 

// messages.h

struct Message_SetPosition 
{
	DECLARE_MESSAGE(SetPosition) // This creates a function returning the identifier to use in the hash map. 

 
	Message_SetPosition(const Maths::Point2D& position_) : position(position_) {}

	Maths::Point2D position;
};

// physics.h

class PhysicsComponent : public ComponentBase
{
public:
	void RegisterHandlers(Entity* parent); // Called once when the component is created.
	
        /// ...

	void HandleMessage(Entity* from, const Message_SetPosition& m);

	/// ...

};

// physics.cpp

void PhysicsComponent::RegisterHandlers(Entity* parent)
{
	REGISTER_HANDLER(parent, PhysicsComponent, Message_SetPosition) // Registering a member function pointer with the parent Entity.
}


// Inside some other component

pEntity->SendMessage(parent, Message_SetPosition(p));


#11 Kylotan   Moderators   -  Reputation: 3338

Like
0Likes
Like

Posted 22 February 2013 - 07:30 AM

Though for later/other projects I really want to get into this message systems but I'v been goggling like crazy and I haven't found any good example other than:

sendMessage(message);

 

 

Well, you can consider there to be 2 types of messaging - direct, and broadcast.

 

The direct kind appears to be what EWClay has shown you, eg. pEntity->SendMessage(parent, Message_SetPosition(p));

 

Personally I don't see much use for that. pEntity still needs to have enough knowledge of its parent to know that it can handle a SetPosition message, so it's not like you've avoided a dependency between the two components - you've just moved it out of the code and into your head. You need to know that these 2 components work together for your system to work correctly. So you may as well have the simpler syntax of getting a reference to the component and accessing it directly. Also, and how does the message passing system handle error situations when the message isn't handled? Or the arguments were wrong for some reason? There don't seem to be any real benefits from this sort of approach, and many downsides (especially in a language like C++ where making new message types is laborious).

 

Broadcast messaging is a bit more useful. Situations can exist where you need any number of associated entities or components to consider acting a certain way - eg. your character makes a noise, so you broadcast a NoiseMadeAt(x,y) message so that NPCs can consider hunting you down or whatever. In that sort of system, you just call a function which iterates over all entities and their components to see if any of them want to handle that message. This is orthogonal to component-based systems however - broadcast messaging is useful for most games regardless of how they're implemented.



#12 CogOfHate   Members   -  Reputation: 112

Like
0Likes
Like

Posted 22 February 2013 - 08:09 AM

I too am trying to design a entity system in this manner.  I am also faced with the same problems.  The one one article that I read and I thought was really good at explaining the idea to me was http://t-machine.org/index.php/2007/09/03/entity-systems-are-the-future-of-mmog-development-part-1/

 

In designing a component system, I gather that the idea is to have all the systems keep a local copy of the components that it will use in some locally allocated array.  This way, the process of iterating over all components within the system will make it more cache coherent.

 

I would try to avoid calling methods on entities as this will incur a LHS (load hit store) and cache misses.  The system should have all the information it needs.  In doing it this way, it should be trivial to start threading the systems on different processes.

 

An idea I am playing around with is having a message queue that is maintained within the engine (or equivalent).

 

Queue looks like:

 

____________________________________

| <EntityID, Component> | <Read|Write> Bit |

____________________________________

| ...                                   |                              |

____________________________________

 

First time through, the queue is empty.  The queue is copied to the system before the update method is called.  If a system modifies a component, it places a message in the queue setting the WRITE bit.

 

The queue is copied back and sent over to the next system.  The next system if it requires the updatd information will check the message queue.  If it reads the component for the entity in question it will set the READ bit.

 

At the end of the frame, after all systems have had their go, we purge all entries in the queue that are marked READ and keep the ones that are marked WRITE.  This way, the previous systems in the big loop through will get the messages from the previous frame. 

 

I'm not sure this is the best way, it's just an approach I'm trying to take.

 

Another good resource is:



#13 rocklobster   Members   -  Reputation: 415

Like
0Likes
Like

Posted 22 February 2013 - 08:26 AM

One thing I might be wary of when using the whole

 

Physics* p = entity.getComponent<Physics*>( ComponentID::PHYSICS ); 

 

idea is that it restricts you to one of each component. Certainly you wouldn't need multiple physics or graphics components but some other component types there might be situations when you need this functionality. 

 

eg.

 

You want to create a tank with two cannons:

 

GameObject* go = factory->NewGameObject();

go->AddComponent(new Cannon());
go->AddComponent(new Cannon());
go->AddComponent(new Animation());
// etc

Canon* canon = go->GetComponent<Canon*>(); // ??

the above could be a problem.


Edited by rocklobster, 22 February 2013 - 08:31 AM.


#14 CogOfHate   Members   -  Reputation: 112

Like
0Likes
Like

Posted 22 February 2013 - 09:08 AM

One thing I might be wary of when using the whole

 

Physics* p = entity.getComponent<Physics*>( ComponentID::PHYSICS ); 

 

idea is that it restricts you to one of each component. Certainly you wouldn't need multiple physics or graphics components but some other component types there might be situations when you need this functionality. 

 

eg.

 

You want to create a tank with two cannons:

 

GameObject* go = factory->NewGameObject();

go->AddComponent(new Cannon());
go->AddComponent(new Cannon());
go->AddComponent(new Animation());
// etc

Canon* canon = go->GetComponent<Canon*>(); // ??

the above could be a problem.

 

Would it make sense in that case to have the cannons as separate entities? They could be attached to the parent and inherit all transform properties and what not.



#15 EWClay   Members   -  Reputation: 659

Like
0Likes
Like

Posted 22 February 2013 - 11:16 AM

Though for later/other projects I really want to get into this message systems but I'v been goggling like crazy and I haven't found any good example other than:

sendMessage(message);

 

 

Well, you can consider there to be 2 types of messaging - direct, and broadcast.

 

The direct kind appears to be what EWClay has shown you, eg. pEntity->SendMessage(parent, Message_SetPosition(p));

 

Personally I don't see much use for that. pEntity still needs to have enough knowledge of its parent to know that it can handle a SetPosition message, so it's not like you've avoided a dependency between the two components - you've just moved it out of the code and into your head. You need to know that these 2 components work together for your system to work correctly. So you may as well have the simpler syntax of getting a reference to the component and accessing it directly. Also, and how does the message passing system handle error situations when the message isn't handled? Or the arguments were wrong for some reason? There don't seem to be any real benefits from this sort of approach, and many downsides (especially in a language like C++ where making new message types is laborious).

 

It's only direct as far as the target entity is concerned. Within that it is a broadcast to all components.

 

Needing to know whether the parent can handle a certain message - nope. The sending code only has responsibility for sending the message. What happens after is the responsibility of the target and its components. Not handling the message is a valid response. Suppose I shoot something and send it a damage message? If the object is not damageable, that's perfectly fine. Parameters are checked. The only error you can really make is to send the wrong message type. It's working pretty well for me.



#16 BeerNutts   Crossbones+   -  Reputation: 2779

Like
0Likes
Like

Posted 22 February 2013 - 12:57 PM

Hi Moonkis,

 

I've been through this myself, and I developed 2 different forms of Component Based Entity Systems (CBES).

 

The 1st one has the Components contain both data and Logic for the component (similar to how yours is setup), and it communicates with other components and entities via events.  It had it's pluses and minuses, but I wasn't satisfied. (Here's the link to part 1 of 4 describing this system).

 

So, I decided to make a CBES where the component only holds data, and Systems perform on the components.  You can find the description of that system here, and the github is here.

 

I'd suggest at least looking through them to get a feel of the 2, if anything just to show how different systems can work.  I personally like the component only holding data, and allowing systems to perform the logic on them , but it's probably just a personal preference of mine.

 

Good luck and have fun.


My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

#17 NightCreature83   Crossbones+   -  Reputation: 2733

Like
0Likes
Like

Posted 22 February 2013 - 01:10 PM


You both are using an aggregation pattern for components only. The component system stores more then just components in the game object it also stores attributes, and it is these you should set which in turn the components can access. If you need inter component communication you should send a message to the currently connected entity and let it pass it done to the rest of it's components, if this component shouldnw't handle it send it to all currently active game objects and let them handle it like the internal message passing works.
 
A component shouldn't need to know anything about any other component or be passed any real information other then the entity in it's update function, see this article and all its previous posts on it http://www.altdevblogaday.com/2011/09/23/the-game-entity-part-v-future-ponderings/
 
I use fast string hash functions in my engine to do the look up of components and attributes that are stored on the entity because name based indexing is easier to read whilst developing. And it also allows me to add components and attributes multiple times under different names.
 
If your entity contained a position attribute you could have fixed this like this:

void KeyboardInputComponent::Update(Entity& entity)
{
    Vector3& position = entity.getAttribute( Hash.HashString( "position" ) ).getValue<Vector3>(); //Or how you store your position
   if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Down ) )
   {
       float ny = y + MOVEMENT_SPEED * dt;
   }
   if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Up ) )
   {
       float ny = y - MOVEMENT_SPEED * dt;
   }
   if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Right ) )
   {
       float nx = x + MOVEMENT_SPEED * dt;
   }
   if ( sf::Keyboard::isKeyPressed( sf::Keyboard::Key::Left ) )
   {
       float nx = x - MOVEMENT_SPEED * dt;
   }
  
    position += Vector3(nx, ny,  0.0f);
}
 
void PhysicsComponent::Update(Entity& entity)
{
    Vector3& position = entity.getAttribute( Hash.HashString( "position" ) ).getValue<Vector3>(); //Or how you store your position
    //Do your collision here with the position you have updated before
}
 
//Or
void PhysicsComponent::respondToPositionChangedMessage(const PositionChangedMessage& msg)
{
   Vector3 position = msg.getPosition(); //Message is sent from the keyboard component
    //Do collision
}


Components holds their variables they are using, Entity is basically just an shell where components lives, it doesn't own any attributes other than an array of components, rather is collected under the same roof. "Movable" haves the attributes float x,y nx, ny it's responsibilities is moving and keeping track of where it is.

Your approach is very interesting though, how does a component send a message? How does a component know what type of message it is? How does it reach the right components if they share they same base-class and lives under an anonymous array?


I have my message sending in the component at the moment which looks like this: (This is C# code because I can be more productive in this in my spare time)
    public abstract class Component : GameComponent
    {
        public Component(Game game) : base (game)
        {
        }

        public abstract void deserialise(BinaryReader stream);
        public abstract void deserialise(XmlNode node);
        public abstract void serialise(BinaryWriter stream);
        public abstract void StartComponent(Entity entity);
        public abstract void Update(Entity entity, InputState input, GameTime elapsedTime);
        public abstract void StopComponent(Entity entity);

        /// <summary>
        /// Handles a message passed along from the enitity it is connected to
        /// To get the entity you have to ask the GameObjectManager to give you the entity indicated in the message,
        /// this keeps the dispatchMessage function generic and could be a static function that handled everything.
        /// </summary>
        /// <param name="message"></param>
        /// <returns>returns whether it has handled the message</returns>
        public abstract MessageResult dispatchMessage(MessageBase message);

        protected void SendMessage(MessageBase message)
        {
            ((MessageHandler)Game.Services.GetService(typeof(MessageHandler))).addMessage(message);
        }
        public int UpdatePriority
        {
            get { return m_updatePriority; }
            set { m_updatePriority = value; }
        }

        public void AddTrace(String format, params Object[] arg)
        {
            DebugWrapper.Trace(GetType().Name, format, arg);
        }

        protected int m_updatePriority;
    }
The message handler is a globally constructed instance that aggregates all listeners for messages and all messages. Update is called once a frame on this and it then goes through the message list and calls dispatch message on each listener passing in the message it is iterating on. It goes through all the listeners until it finds one that returns messageDispatched or until it reaches the end of the listener list.

I do use a base message as an OO hierarchy isn't bad it is still a useful tool, and as such I have base classes for my Component and Attributes as well. All the Component pattern tries to do is make behaviours pluggable and avoids diamonds in your OO hierarchy diagram that exist in traditional GameObject systems.

Btw I do have to mention this I only create one Component of each type, so the entity only has a reference to a component it doesn't own it. The component can actually create attribute handles on the entity it is attached to if it needs to be able to operate on them but it only does this in the start component method which is called when it gets attached to a particular entity. This causes me to have to use a blackboard type approach to the system.

Edited by NightCreature83, 22 February 2013 - 03:57 PM.

Worked on titles: CMR:DiRT2, DiRT 3, DiRT: Showdown, GRID 2, Mad Max

#18 Kylotan   Moderators   -  Reputation: 3338

Like
0Likes
Like

Posted 22 February 2013 - 02:21 PM

Needing to know whether the parent can handle a certain message - nope. The sending code only has responsibility for sending the message. What happens after is the responsibility of the target and its components. Not handling the message is a valid response. Suppose I shoot something and send it a damage message? If the object is not damageable, that's perfectly fine. Parameters are checked. The only error you can really make is to send the wrong message type. It's working pretty well for me.

The code itself is robust in the face of errors but the behaviour is not. By removing the direct link between A and B you also remove any real ability for A to make any useful assumptions about B. You send off the message and hope. And when your program fails to work, you often have no idea why. Maybe you sent the message to the wrong entity. Maybe you sent the wrong type of message. Maybe the receiving entity didn't have the right component to handle it. Maybe it had the right component but was set to receive the wrong type of message.

With a direct link, like Unity's GetComponent, this is rarely a problem. Message type problems are eliminated at compile time and failure to find the right component will result in an instant exception.

On top of this, without any links between components, it's virtually impossible to perform useful querying. I've seen people work around this by treating the entity as a giant blackboard of attributes all all sorts of implicit coupling between components based on these. Instead of clear and precise interfaces exposed by components, you have the almost global data of a map of named (and often typeless) attributes. May as well have one single MegaActor class and throw all the functionality in there.

I can't imagine that forbidding the direct access and forcing everything to go via messages could do anything other than make the code more complex all round.

#19 EWClay   Members   -  Reputation: 659

Like
0Likes
Like

Posted 22 February 2013 - 03:38 PM

Well, I don't seem to be the only one using messages but I'm happy to argue the case.

Compared to calling a function directly, what do I lose? Only the one thing I want to gain, which is not to have to care which component, if any, handles the message. And if I do want that, I don't forbid direct access to components, I just don't like to use it all the time.

I don't like the giant blackboard idea. I use tags the way Unity does, for quick access to basic information. I try to separate responsibilities so that I don't have to pass much data around.

More complex? No. Sending a message takes less code than getting pointers to the component(s) required, checking them and calling a function.

#20 CogOfHate   Members   -  Reputation: 112

Like
0Likes
Like

Posted 22 February 2013 - 06:18 PM

Hi Moonkis,

 

I've been through this myself, and I developed 2 different forms of Component Based Entity Systems (CBES).

 

The 1st one has the Components contain both data and Logic for the component (similar to how yours is setup), and it communicates with other components and entities via events.  It had it's pluses and minuses, but I wasn't satisfied. (Here's the link to part 1 of 4 describing this system).

 

So, I decided to make a CBES where the component only holds data, and Systems perform on the components.  You can find the description of that system here, and the github is here.

 

I'd suggest at least looking through them to get a feel of the 2, if anything just to show how different systems can work.  I personally like the component only holding data, and allowing systems to perform the logic on them , but it's probably just a personal preference of mine.

 

Good luck and have fun.

 

Your second implementation is how I'm implementing my ES as well.  Systems contain the logic, components hold the data and entities are nothing more than an identifier.   I too am using message passing in which the Systems subscribe to which messages they are interested in.

 

The reason I chose to break the components and systems up in this manner was to make it easier to thread them.  The systems are self-contained and components don't know about other components making this job a little easier.






Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS