2
\$\begingroup\$

I've done my research and read as many articles and posts on messaging and ECS's as I could find to help me get mine working. However, I'm at a roadblock on how to get my messag functionality to be handled correctly.

A little information on my ECS system.

Pieces:

Entities - Generic objects that only contains a list of attached components, has a few methods to ask if it has certain components and to retrieve them.

Components - Only holds data specific to their nature and all have a bitMask denoting the component type.

Systems - Functionality code specific to their nature and keeps a list of components it will process.

Messages - Messages are currently created via the MessageInfo class. A message can be created easily using this line of code:

MessageInfo* msg = new MessageInfo(MESSAGE_TYPE, LISTENER_ENTITY, boost::ANY PARAMS);

I am trying to keep things decoupled as possible and have thought about adding a static MessageCenter that I can push messages to which will then either dispatch them immediately or throw them into a queue (depending on the push function call).

Example being that a message is created and pushed to the message center. The message center then immediately looks through its map of systems that care about X message and dispatches the message to a "RecieveMessage" method that's inherent to all systems. This approach sounded really good to me, so I started to develop it on paper and ended up running into a wall.

Let me put forth one of the scenario's I ran up against.

Scenario:

I have a text label entity being used as a % indicator with the position (x/y coords) component and text component (string, font size, etc). I want to alter some data properties of this entity, such as the text displayed and its color.

Since I know that all systems have a recieveMessage method I am quickly able to encapsulate functionality specific to those types of requests as shown below.

void SOMESYSTEM::RecieveMessage(MessageInfo* messageInfo)
{
    switch(messageInfo->GetMessageType())
    {
    case MessageType::CHANGE_TEXT:
        {
            boost::any params = messageInfo->GetParams();
            if(params.type() == typeid(std::tuple<BaseComponent*,std::string>))
            {
            auto param = boost::any_cast<std::tuple<BaseComponent*,std::string>>(params);
            TextComponent* textComponent = static_cast<TextComponent*>(std::get<0>(param));
            textComponent->SetTextString(std::get<1>(param));
        }
    }break;
case MessageType::CHANGE_TEXT_COLOR:
    {
        boost::any params = messageInfo->GetParams();
        if(params.type() == typeid(std::tuple<BaseComponent*,sf::Color>))
        {
            auto param = boost::any_cast<std::tuple<BaseComponent*,sf::Color>>(params);
            TextComponent* textComponent = static_cast<TextComponent*>(std::get<0>(param));
            textComponent->SetCurrentColor(std::get<1>(param));
            }
        }break;
    }
}

The wall I run into is where do I put this functionality? Currently the only system that touches text components is my RenderTextSystem. It wouldn't seem proper to implement that RecieveMessage function wanting those specific messages. That system just draws the text to the screen. I could be over-engineering here though.

I have other scenarios similar to this and figured if I could solve this one, I could end up solving 99% of the others.

\$\endgroup\$
2
  • \$\begingroup\$ Is boost::any performing well for message system data? It might slow down if you put tons of messages I think. If you end up doing switch case maybe boost::variant? Sorry for offtopic. \$\endgroup\$
    – Lufi
    Commented Feb 13, 2014 at 8:47
  • \$\begingroup\$ Right now it works pretty well without issue. I can always refactor it if performance starts to become an issue. \$\endgroup\$ Commented Feb 13, 2014 at 17:10

1 Answer 1

2
\$\begingroup\$

Put this functionality wherever you want. Sure strict and "pure" ECS says don't put things in your components in order to avoid OOP. This usually just hinders everyone still learning from thinking about how to write useful code.

You clearly understand how to implement a form of messaging, so just use it and don't waste time trying to adhere to some arbitrary design pattern. This functionality can be placed into components, the game object itself, engine systems, or anywhere else.

Messaging is just a generic way to send data from one place to another. Usually a great benefit for first implementations of messaging is to try and cut down on file inclusions and inter-file dependencies.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Kind of what I needed to hear, I tend to over engineer when I write code. Always worrying if what I wrote is really in the best spot or not and end up taking more time then I should on certain implementations. \$\endgroup\$ Commented Feb 13, 2014 at 17:11
  • \$\begingroup\$ This is great advice. It works extremely well on small projects, but as software grows, no matter which design patterns you choose, complexity gets scary! The only way to get good at programming is to program things, keep pushing the limits, refactor often, and learn from past mistakes. \$\endgroup\$
    – Dan
    Commented Nov 6, 2022 at 20:11

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .