A Simple Message Bus in Unity

postman

The idea of having many decoupled components in a system, with some sort of message bus to facilitate communication is nothing new. It gets used all the way from small applications to complex and distributed software systems. It has become especially useful in Startup Freak because of how interconnected many of the game systems are. Hiring an employee, for example, causes multiple UI components as well as the simulation to change.

In this post I’ll go over my implementation of a messaging broker in Unity.

Implementation

It’s actually quite a basic implementation. The interface is loosely based on CaliburnMicro’s IEventAggregator and looks like this:


public interface IMessage
{
}

public interface IHandle<T> where T : IMessage
{
  void Handle(T message);
}

public interface IMessenger
{
  void Subscribe(object subscriber);
  void Unsubscribe(object subscriber);
  void Publish<T>(T message) where T : IMessage;
}

Messages are marked with an interface. Other objects (like MonoBehaviours) can subscribe to the messenger in order receive messages. As we’ll see in the next section, a class shows its interest in a particular type of message by using the IHandle<> interface. Finally, any part of the code can publish a message, which in turn notifies all subscribing objects.

The actual implementation of IMessenger simply keeps track of subscribing objects in a dictionary. You can see the source code here.

Usage

To make this a little more concrete, I’ll use an example from Startup Freak. At the end of each game turn (referred to as a “sprint”), various parts of the game need to be refreshed and updated. One of these is the player’s company finances. We first define the message itself:


public class SprintCompleted : IMessage
{
  public SprintCompleted(int number)
  {
    Number = number;
  }

  public int Number { get; private set; }
}

The message can have any number of parameters as needed; in this case the sprint number. Next we need the Company class to subscribe to this message:


public class Company : IHandle<SprintCompleted>
{
  public Company(IMessenger messenger)
  {
    messenger.Subscribe(this);
  }

  public void Handle(SprintCompleted message)
  {
    RecalculateFinances()
  }

  ...
}

Finally the Sprint class publishes the message:


public class Sprint
{
  IMessenger _messenger;

  public Sprint(IMessenger messenger)
  {
    _messenger = messenger
  }

  ...

  public void OnTasksCompleted()
  {
    _messenger.Publish(new SprintCompleted());
  }
}

Couple of things to note here:

  • It’s perfectly reasonable for a class to implement multiple IHandle<> interfaces.
  • When it comes to accessing the instance of IMessenger you have a couple of options. You can either use a singleton/static instance (I won’t get into the details of why that’s less than ideal), or you can use Dependency Injection and inject it into the constructor (or another method/property in the case of a MonoBehaviour). I use Zenject for this project which is a very solid choice for Unity.

Gotchas

A few things to be aware of:

  • To keep things simple this a synchronous implementation. That means when you publish a message, the handlers are called immediately and synchronously.
  • Related to the above, care must be taken in what the handlers do. For example you can get into a situation where a chain of messages and message handlers create an infinite loop.
  • If multiple handlers subscribe to the same message, there is no guarantee of arrival order. Handlers should function independently of the order.
  • Often subscribers are alive for the entirety of the scene or game, which makes calling Unsubscribe() more or less unnecessary. However for any subscribers with shorter life spans, it’s important to do this. Otherwise you may run into memory leaks or have handlers try to access previously destroyed GameObjects.

Source

Again, you can see the implementation of the messenger here.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s