Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm making a simple game, and have decided to try to implement a messaging system.

The system basically looks like this:

Entity generates message -> message is posted to global message queue -> messageManager notifies every object of the new message through onMessageReceived(Message msg) -> if object wants, it acts on the message.

The way I'm making message objects is like this:

//base message class, never actually instantiated
abstract class Message{
  Entity sender;
}

PlayerDiedMessage extends Message{
  int livesLeft;
}

Now my SoundManagerEntity can do something like this in its onMessageReceived() method

public void messageReceived(Message msg){
  if(msg instanceof PlayerDiedMessage){
     PlayerDiedMessage diedMessage = (PlayerDiedMessage) msg;
     if(diedMessage.livesLeft == 0)
       playSound(SOUND_DEATH);
  }
}

The pros to this approach:

  1. Very simple and easy to implement
  2. The message can contain as much as information as you want, because you can just create a new Message subclass that has whatever info necessary.

The cons:

  1. I can't figure out how I can recycle Message objects to a object pool, unless I have a different pool for each subclass of Message. So I have lots and lots of object creation/memory allocation over time.
  2. Can't send a message to a specific recipient, but I haven't needed that yet in my game so I don't mind it too much.

What am I missing here? There must be a better implementation or some idea that I'm missing.

share|improve this question
    
1) Why do you need to utilize those object pools, whatever they are? (On a side note, what are they and what purpose do they serve?) There's no need to design beyond what works. And what you have currently appears to have no problems with that. 2) If you need to send a message to a specific recipient, you find it and you call its onMessageReceived, no fancy systems required. –  snake5 Oct 11 '12 at 6:33
    
Well, I meant that I can't currently recycle the objects into a general purpose object-pool. So if I ever want to reduce the memory allocations necessary, I can't. Right now this isn't really an issue because my game is so simple that it runs perfectly fine as it is. –  you786 Oct 11 '12 at 6:39
    
I'm assuming this is Java? Looks like you're trying to develop an event system. –  Justin Skiles Oct 11 '12 at 15:59
    
@JustinSkiles Correct, this is Java. I guess you're right, it really is used to act as an event system. What other ways can you use a messaging system? –  you786 Oct 13 '12 at 3:03
add comment

4 Answers

Simple answer is you don't want to recycle messages. One recycles objects that are either created (and removed) by thousands every frame, like particles, or are very heavy (tens of properties, long initialization process).

If you have a snow particle with some bitmap, velocities, physical attributes associated that just went down below your view, you may want to recycle it instead of creating a new particle and initializing the bitmap and randomizing attributes again. In your example there's nothing useful in the Message to keep it, and you will waste more resources on removing it's instance-specific properties, than you would just on creating a new Message object.

share|improve this answer
3  
Good answer. I'm using a pool for my messages, and I never actually investigated whether or not they were useful. (Past-)time to profile! –  Raptormeat Oct 11 '12 at 8:13
    
Hmm. What if I have a message that is emitted maybe every frame? I guess the answer would be to have a specific pool for that kind of object, which makes sense. –  you786 Oct 13 '12 at 3:02
    
@you786 I just made a quick dirty test in Actionscript 3. Creating an Array and populating it with 1000 Messages takes 1ms in a slow Debug environment. I hope that clears things up. –  Markus von Broady Oct 13 '12 at 8:17
    
@you786 He is correct, first identify this as a bottleneck, if you want to be an indie game developer, you need to learn to make things fast and effectively. Investing time in fine tuning your code is only worth it if that code is currently slowing down your game and make these changes can boost up performance significantly. For instance, creating Sprtie instances in AS3 would be very time consuming, it is much better to recycle them. Creating Point objects is not, recycling them would be a waste of coding time and readability. –  Arthur Wulf White Oct 13 '12 at 17:42
    
Well, I should have made this more clear, but I'm not worried about the allocation of memory. I'm worried about Java's garbage collector running in the middle of the game and causing slowdowns. But your point about premature optimization is still valid. –  you786 Oct 14 '12 at 16:48
show 1 more comment

In my Java-based game, I came up with a very similar solution, except my message handling code looks like this:

@EventHandler
public void messageReceived(PlayerDiedMessage msg){
  if(diedMessage.livesLeft == 0)
     playSound(SOUND_DEATH);
}

I use annotations to mark a method as an event handler. Then, on game start, I use reflection to compute a mapping (or, as I call it, "piping") of messages - which method to invoke on which object when an event of a specific class is sent. This works... awesome.

The piping calculation can get a bit complicated if you want to add interfaces and subclasses to the mix, but it's fairly straightforward nonetheless. There is a slight slowdown during game load when all the classes need to be scanned, but it's done only once (and probably can be moved to a separate thread). What's gained instead is that actual sending of messages is cheaper - I don't have to invoke every "messageReceived" method in the codebase just to have it check if the event is of a good class.

share|improve this answer
3  
So you basically found a way for your game to run slower? :D –  Markus von Broady Oct 11 '12 at 7:40
2  
@MarkusvonBroady If it's once on load, totally worth it. Compare that to the monstrosity in my answer. –  michael.bartnett Oct 11 '12 at 8:04
1  
@MarkusvonBroady Invocation through reflection is only slightly slower than normal invocation, if you have all the pieces in place (at least my profiling doesn't show any problem there). Discovery is costly, for sure. –  Liosan Oct 11 '12 at 8:16
    
@michael.bartnett I code for a user, not to myself. I can live with my code being bigger for faster execution. But that was just my subjective remark. –  Markus von Broady Oct 11 '12 at 8:18
    
+1, the annotations path is something I never thought of. So just to clarify, you'd basically have a bunch of overloaded methods that are called messageReceived with different subclasses of Message. Then at runtime you use reflection to create some sort of map that computes MessageSubclass => OverloadedMethod? –  you786 Oct 13 '12 at 3:01
show 1 more comment

EDIT Liosan's answer is sexier. Look into it.


Like Markus said, messages aren't a common candidate for object pools. Don't bother with it for the reasons he mentioned. If your game sends tons of messages of specific types, then maybe it'd be worthwhile. But then at that point I'd suggest you switch over to direct method calls.

Speaking of which,

Can't send a message to a specific recipient, but I haven't needed that yet in my game so I don't mind it too much.

If you know a specific recipient you want to send it to, wouldn't it be pretty easy to get a reference to that object and do a direct method call on it? You could also hide specific objects behind manager classes for easier access across systems.

There's one con to your implementation, and it's that there's the potential for lots of objects to get messages that they don't really care about. Ideally your classes would only receive messages that they actually care about.

You can use a HashMap<Class, LinkedList<Messagable>> to associate message types with a list of objects that want to receive that type of message.

Subscribing to a message type would look something like this:

globalMessageQueue.subscribe(PlayerDiedMessage.getClass(), this);

You could implement subscribe like so (forgive me some mistakes, haven't written java in a couple of years):

private HashMap<Class, LinkedList<? extends Messagable>> subscriberMap;

void subscribe(Class messageType, Messagable receiver) {
    LinkedList<Messagable> subscriberList = subscriberMap.get(messageType);
    if (subscriberList == null) {
        subscriberList = new LinkedList<? extends Messagable>();
        subscriberMap.put(messageType, subscriberList);
    }

    subscriberList.add(receiver);
}

And then you could broadcast a message like this:

globalMessageQueue.broadcastMessage(new PlayerDiedMessage(42));

And broadcastMessage could be implemented like this:

void broadcastMessage(Message message) {
    Class messageType = message.getClass();
    LinkedList<? extends Messagable> subscribers = subscriberMap.get(messageType);

    for (Messagable receiver : subscribers) {
        receiver.onMessage(message);
    }
}

You could also subscribe to message in such a way that you can split up your message handling into different anonymous functions:

void setupMessageSubscribers() {
    globalMessageQueue.subscribe(PlayerDiedMessage.getClass(), new Messagable() {
        public void onMessage(Message message) {
            PlayerDiedMessage msg = (PlayerDiedMessage)message;
            if (msg.livesLeft <= 0) {
                doSomething();
            }
        }
    });

    globalMessageQueue.subscriber(EnemySpawnedMessage.getClass(), new Messagable() {
        public void onMessage(Message message) {
            EnemySpawnedMessage msg = (EnemySpawnedMessage)message;
            if (msg.isBoss) {
                freakOut();
            }
        }
    });
}

It's a little verbose, but helps with organization. You could also implement a second function, subscribeAll that will keep a separate list of Messagables that want to hear about everything.

share|improve this answer
    
+1, thanks for all the suggestions. –  you786 Oct 13 '12 at 2:58
add comment

1. Don't.

Messages are low cost. I agree with Markus von Broady. GC will collect them fast. Try not to attach much additional info for messages. They are just messages. Finding instance of is an info by itself. Use it (as you did in your example).

2. You could try using MessageDispatcher.

In contrast to first solution you could have centralized message dispatcher that processes messages and passes them to intended objects.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.