Component based system, doing it right?

Started by
18 comments, last by CogOfHate 11 years, 1 month ago

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.

Advertisement

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:

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.

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.

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.

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)


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.

Worked on titles: CMR:DiRT2, DiRT 3, DiRT: Showdown, GRID 2, theHunter, theHunter: Primal, Mad Max, Watch Dogs: Legion

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

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.

This topic is closed to new replies.

Advertisement