Sign in to follow this  
M4573R

How much should I be using messages?

Recommended Posts

I always hear that you HAVE to use a messaging system on any kind of complex game. Personally I find that a lot of things I don't want to use messages for. For example: Why would I want to send key press messages to a ton of game objects, when their logic can just ask if a specific key is pressed from the input singleton? When I would want to use messages, however, would be if multiple systems or objects all respond to the same event, like an initialize message, or cleanup, etc. So to get to the actual question: How much do you guys use messages and for what sized projects? As another example, let's say I have a component based engine, and I want to move something. What would be the advantage over sending a message to the entity / position component itself, when I could just ask for that component and set it directly. If other components rely on position, they can also just ask the position component. The reason I'm asking this is because I'm starting to re-factor a small engine I made and would like to keep things robust. I'm trying to get a feel for how messaging I should plan for ( system only, every entity, every component ). As always, thank you gamedev users for you infinite wisdom.

Share this post


Link to post
Share on other sites
Concurrency. It allows the whole engine to be distributed across multiple cores, and using messages avoids having to lock state.

Using messages instead of immediate invocation can also play nicer with typical simulation model, where all events that occur in current step aren't processed immediately, but handed off to next tick. This can make simulation somewhat more consistent, and may even reduce work.

Collision detection is an example. Rather than affecting state directly, movement messages are generated without changing current state. This saves having to rebuild and retest collision state each time position is touched, since it is known that current state will not be changed until we are done with it.

Share this post


Link to post
Share on other sites
Collision is a very good example I hadn't thought about. Are there any places I should avoid using messages? Possibly someplace it would just be way too much overhead?

Share this post


Link to post
Share on other sites
For starters, what does your idea of a "messaging system" look like? Both of the following are ways you can send messages, but often people overlook the fact that #2 below is also a way of processing messages:
// #1
ExplosionDamageMsg msg( position, 42.0f );
SendMessage( playerID, msg );//Tell a specific player object about the explosion
SendMessage( BROADCAST_ALL_PLAYERS, msg );//Tell all player objects about the explosion
// #2
player->ExplosionDamage( position, 42.0f );//Tell a specific player object about the explosion
playerSystem->ExplosionDamage( position, 42.0f );//Tell all player objects about the explosion

Quote:
What would be the advantage over sending a message to the entity / position component itself, when I could just ask for that component and set it directly. If other components rely on position, they can also just ask the position component.
This question basically boils down to whether you want to "push" the data from one system to the next, or let systems "pull" in the data that they need themselves.

Example #1: some kind of spontaneous event, like "explosion damage" or "hit by a car".
Using push, when the event occurs, you tell any interested parties (things that can be effect by that event type) about it.
OnExplosion:
send [DamageMsg] to all objects in radius
Using pull, any interested parties have to constantly ask everything else if this kind of event has occurred, which is pretty inefficient.
OnUpdate:
for each object
for each explosion in explosion-manager in radius
Process Damage
Seeing these things happen randomly and unpredictably, it's probably more efficient to just run the "do I need to take damage now" code in response to the event happening, instead of every single frame. So, for spontaneous events, pushing is usually better.

Example #2: some kind of predictable event, like "dynamic object moved" or "health recharged".
Seeing these things happen almost every frame, it's probably more efficient to skip the overhead of creating a message, and just check for this condition constantly. So, for predictable events, pulling is usually better.


I prefer to let things "pull" as much data as possible, because it's usually easier. However, as Antheus said, "pushing" data via messages is great if you're writing concurrent systems, as it creates communication "channels". If you're "pulling" lots of data in a concurrent system, then that means threads are likely sharing state, which is usually bad... There are ways of enabling "pull" in a concurrent system though. In my concurrent component system, I've got 3 different options for this communication:

Option 1 - Messages (Push the data)
-- "Compute position" tasks put their newly created data into messages. At an appropriate point in time, "consume position" tasks process these messages.
-- Cons: Lots of memory allocations for messages.
Option 2 - Double buffered state (Pull the data)
-- Create two copies of position. "Compute position" task can be writing to one copy, while "consume position" tasks can be reading from the other copy. At the end of the frame you copy the new data over the old data.
-- Cons: Increased memory usage, extra latency.
Option 3 - Serial task groups (Pull the data)
-- compute all positions, then compute things that depend on position afterwards.
-- Cons: Reduces parallelism; the two task groups can't execute at the same time.

Share this post


Link to post
Share on other sites
Message systems are generally used to decouple systems, as well as decoupling action from reaction, and they are emminantly useful for one-to-many (broadcast) communications, fire-and-forget, or temporary one-to-one communication channels.

They also tend to give you some means of controlling execution priority, which can be a handy feature, and is otherwise limited by the structure of your code unless you have another queuing mechanism.

Share this post


Link to post
Share on other sites
Quote:

Why would I want to send key press messages to a ton of game objects, when their logic can just ask if a specific key is pressed from the input singleton?

Having a message stream makes it trivial to:
*Inject messages for cutscenes or other specially triggered events.
*Record messages to have a playback feature. For instance look at the new ratchet & clank, and their time replay mechanic. Or at starcraft and their replay system.
*Treat all messages in a uniform manner, even if the message originated local or over the network. The base code only has to handle "recieved press 'x'", and not care what subsystem it came from.

Share this post


Link to post
Share on other sites
The usual advice stands: try the simple way first. If it's insufficient, you'll generally find out fairly quickly.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this