How much should I be using messages?

Started by
6 comments, last by Zahlman 14 years ago
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.
Advertisement
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.
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?
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:
// #1ExplosionDamageMsg msg( position, 42.0f );SendMessage( playerID, msg );//Tell a specific player object about the explosionSendMessage( BROADCAST_ALL_PLAYERS, msg );//Tell all player objects about the explosion// #2player->ExplosionDamage( position, 42.0f );//Tell a specific player object about the explosionplayerSystem->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.
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.

throw table_exception("(? ???)? ? ???");

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.
Replays are also a very good example, I guess most of it depends on exactly what I'm implementing.
The usual advice stands: try the simple way first. If it's insufficient, you'll generally find out fairly quickly.

This topic is closed to new replies.

Advertisement