Components: It's never so easy.
The big goal of the component based design is to have these components exist functionally isolated from each other.
However, these components still need to be able to communicate information.Ā Graphics's animations will be based off of what it's doing, such as running if the entity is moving, If collision detects you bumped into a health power up, health needs to be informed to heal, or if health goes down to zero, a lot of things will need to know you'veĀ died.
The strength of components is their flexibility. If you wire a direct reference to another component (say the graphics gets a pointer to a physics) you lose that big advantage. Now you have to have a physics component. This coupling gets rapidly worse the more types of components that get added in.
But there's more than one way to find out important information. Instead of going aroundĀ askingĀ for information and being prepared toĀ answer,Ā having to deal with finding the right people to ask and those people possibly missing, weĀ announceĀ important information, andĀ listenĀ for the stuff we care about.
When something significant happens, you send a message to the entity. The entity then broadcasts this message out to the components. If it's something it cares about, you react. Otherwise, you just ignore it.
The funny thing is, this is where my brain got stuck for a while. I read again and again about this 'messaging' concept, but couldn't seem to figure it out. If I wanted to send a message about landing on the ground, I figured I needed to have something like this:
Entity
{
Ā send( Message m )
Ā { Ā for each component: receive( Message m) }
}
Component
{
Ā Ā receive( Message m)
Ā Ā { Ā ..// read message then react if necessary}
}
This was akin to writing an actual letter, sending it to a supervisor, then having him show the message to every else. The message class itself started being the weird part because I would almost certainly need to pass some contextual information along with the message... a collision message would likely need to say where it occurred, a damage message how much...
These message might need to be stored if it's something that couldn't be resolved immediately... in which case all these new messages are fragmenting memory...
Well. I realized I was derping... I didn't have to make a message object at all.
sendMessage( int messageType, float x, float y)
receiveMessage( int messageType, float x, float y)
{
Ā Ā if( messageType ==Ā something I care about)
Ā Ā { Ā do stuff }
Ā Ā if( messageType == something else I care about)
Ā Ā { do Ā other stuff }Ā
}
This is perfectly fine for a collision message. If the component needs to store something, or queue it up, the component itself can do it. Say this graphics component needs to know if onGround, you give it a boolean flag for it, and if you receive an 'MessageType' equal to 'ON_GROUND', set it to true. When you go to draw, you reference your internalĀ note,Ā which won't change until some yells that you're OFF_GROUND.
If I need to send a message with a different structure, well, just make an additional send() for the Entity and complimentary receive() for the components.
Most messages will probably be ignored, which is fine.
It's also only another step to pass the messages along to something that say, doĀ Achievements. Rather than have something that searches all over the place for relevant info, or inserting theĀ achievementĀ code into other parts of the code base, you can simply have an achievement component andĀ listen.
Right now I'm in the process of converting from my more direct method to this messaging system. The conversion is actually going pretty smoothly. It's... surprisingly a lot more legible having basic update in a component and responding to relevant messages in another area, than to have it all in one place.