A Useful Messaging System

The ability to send game events as messages is a very useful thing indeed. In our projects, we produce different game systems at different times. Sound might be last, or we might add in a scoring system long after the initial game logic is completed. Messages are ideal for this type of scenario.

For instance, take the event of a raptor dying in Raptor Safari. One option–a bad one–is to explicitly reference all neccesary managers in the raptor scripts themselves. So, to add sound we would have to add a call to our sound manager, to add scoring we call a function on our scoring manager, and so on. The problem is we shouldn’t have to edit older scripts to enable functionality in new managers. This is a recipe for errors and programmer collisions (it will be harder to add in real raptor-specific functionality if there is a constant stream of edits to support other parts of the game).

A better paradigm is to have the raptor broadcast a message about its death. This message includes all relevant information: What killed the raptor, where it died, a reference to the raptor object itself, and so on. The message is broadcast with a specific category, like “raptor”, and interested parties sign up to consume messages about this category.

This means we can add functionality about an event without editing the original script. We could add a sound for our raptor’s death, include it in our scoring systems, spawn a particle effect, and more–all without touching the original raptor logic. Perfect! But how do we accomplish this is UnityScript?

C# includes a mechanism to do this called delegates, which is a type that references a method or methods. Unity’s JavaScript doesn’t support delegates, however, and in fact we’re better served with our own system anyway, since we can do additional logic where necessary. Here’s our solution:

Messenger Singleton

We need a single object to keep track of our lists of listeners. When a script wants to listen to some classification of events, like “item” events, it has to call a function on our messenger. The messenger then keeps track of which scripts should recieve messages for which types of events. This looks like:

Messenger.instance.Listen(“item”, this);

The actual collection of listeners is very basic. We use a Hashtable of ArrayLists. The Listen() function looks like:

/**
* Adds a listener for a particular type of message
*/

function Listen(listenerType:String, go:GameObject)
{ 
   // if there’s no array for this tracking category, make a new one
   if(listeners[listenerType] == null)
   {
      listeners[listenerType] = new ArrayList();
   }
   
   var listener:ArrayList = listeners[listenerType];
   
   // only add to the array if it isn’t already being tracked
   if(!listener.Contains(go))
   {
      listener.Add(go);
   }
}

/**
* Register implicitly with a Component instead of GameObject
*/

function Listen(listenerType:String, component:Component)
{
   Listen(listenerType, component.gameObject);
}

We use a second function signature that accepts a Component to allow scripts to use this to register as a listener.

Message Class Structure

All messages are their own classes. To send a message you simply instantiate the class. In Minotaur China Shop, a broadcast of the item delivered message looks like:

new MessageItemDelivered(item, this);

In this particular case, the message contains the item we delivered to a custom (the first argument), and the customer to which it was delivered (the second argument). Let’s take a look at the actual MessageItemDelievered.js file:

#pragma strict

class MessageItemDelivered extends Message
{
   // the item that was collected
   var item:Item;

   // which customer we delievered it to
   var customer:BaseCustomer;
   
   function MessageItemDelivered(item:Item, customer:BaseCustomer)
   {
      this.item = item;
      this.customer = customer;
      
      super(“item”);
   }
}

We use our message objects to store multiple references and values. Many times we will compute additional values. For instance, for a raptor death event we may calculate the impact of the event as a scalar from 0..1 and store that in the message. If other scripts want to know the severity of the impact they can use this value, while leaving us the flexibility to tune it in a single location. This is one reason delegates wouldn’t serve us very well.

The super(“item”) line calls the constructor on the parent class, Message. This is where we stick our category identifier. You can think of the line as saying “our message is now ready, send out to anyone interested in the item category”.

Receiving Messages

You’ll notice we don’t actually define the function name the message is calling anywhere. We do this implicitly based on the class name. The naming standards we use are messages look like MessageItemDelivered and their corresponding function names look like _ItemDelivered(). The leading underscore lets us know at a glance that the function will be called by the messenger.

The messenger calls the function and sends it the instance of the message. This allows messages to contain as much data as we want, and also to precompute anything expensive once (at message composition instead of per-listener). A listening object might look like:

/**
* Score an item being delievered
*/

function _ItemDelivered(msg:MessageItemDelivered)
{
   // do something here
   Debug.Log(“customer purchase # “ + msg.customer.itemsPurchased);
}

Because our code editing environment includes JavaScript autocomplete, it’s very easy to write these functions and see which variables a message provides without having to look it up.

Project Organization

One of the nice side effects of using one class per message is visible organization. We keep all of our messages in one folder, so it’s very easy to see which messages are available in a project. This allows collaborators to get an idea of how much functionality is exposed by other systems. If we need to create a new message–the sound designer would like to hook into something else–it’s easy to go in and add new messages, which may prove useful for other functionality later.

Adding New Messages

Adding a new message is relatively painless. We must:

  • Create a new MessageSomething class (we copy/paste an existing message)
  • Specify the category in the super() call
  • Listeners register with the Messenger singleton
  • Listeners implement _Something(msg:MessageSomething) function

Performance

Our actual broadcast functionality uses Unity’s SendMessage() function. This isn’t nearly as fast as calling functions directly with your code, but in practice it’s fast enough. You wouldn’t want to be calling multiple messages every frame, but for game events–enemy spawned, enemy killed, powerup acquired–it’s performant enough to be practical.

In our current implementation we also let null values accumulate in the ArrayLists of listeners. This hasn’t caused any problems, because we flush the Messenger along with scene changes. If you are incredibly performance-conscious, or developing for the iPhone, you may want to skip a messaging system altogether. The point of a messaging system isn’t programming performance, but production performance; it’s a fast and efficient way to get things done.

Sample Project

It’s not as complicated as it sounds, actually. In practice we create new messages in just a few minutes, and we haven’t updated the actual Messenger logic in two projects. Here’s a sample project to get you started. If you make any improvements or changes to the project, please let us know by leaving a comment!

Download Sample Project

This entry was posted in Scripts and tagged , . Bookmark the permalink.

6 Responses to A Useful Messaging System

  1. Nice framework, very similar to what Mike from Hangout presented at Unite. They’re using C# BTW, with generics for the message type; that adds compile-time consistency check, which is pretty nice I think.
    I think they registered per message instead of per category, but I’m pretty sure you could do it too (haven’t scrupulously read your code though):
    * make extra category classes, that would be pretty much dummies
    * register with Messenger.instance.Listen(Item, this);
    * fire with super(Item);
    In UnityScript you don’t even need typeof() so you actually save the 2 quote characters 😉
    Now that I think of it, your category classes could even inherit from Messenger so you’d go Item.instance.Listen(this)… BTW why do you need an instance? Do you need the messenger to be on scene, can’t it just be static?

    ….. of course all these frameworks seem crude when you’ve tasted the beauty of true .NET events, associated with IDE support! 😉
    But I agree it’s a lot of groundwork, and somehow I’ve always stalled being the first one to try it with Unity, I think the webplayer doesn’t like events too much… plus obviously there’s no IDE support.

  2. ELMo says:

    Any chance that you guys will release a sample project written in Unity 2.6? I’m trying to break into Unity programming, and this seems very helpful, but the system of messages is hard to understand without a working demo (Unity 2.6 isn’t backwards compatible).

  3. Matthew Wegner says:

    Unity 2.6 can open this project. It is backwards compatible (it’s just that older versions aren’t forwards compatible–if we released a 2.6 project people with 2.5 couldn’t open it).

  4. Jason Larabie says:

    I think this should be added to the Unity Wiki. I’ve rebuilt this in C# and have it working. If you decide to put it on Unity Wiki and want it let me know via email.

  5. Mathieu says:

    Have you updated this script for Unity3d 3.0?
    I’m having some errors:
    Assets/_scripts/_messaging/Messenger.js(44,39): BCE0022: Cannot convert ‘Object’ to ‘System.Collections.ArrayList’.

    I really like this messaging system, I hope I’ll still be able to use it :)

  6. Frank says:

    but i think it can be handled internally with Unity 3.0 messages (SendMessage and BroadCastMessage send to all children who have handlers for the message (effectively subscribing them)

Comments are closed.