A typesafe, lightweight Unity message bus system that respects the Open-Closed principle.
For starters, import the package located in the Releases section into your project.
public readonly struct PlayerSpawnMessage
{
public readonly int Health;
public readonly float Speed;
}
Structs are preferred to reduce GC work and because messages will 99% of the time only contain data.
If the message doesn't require data, you can have an empty class or struct too.
public readonly struct PlayerDeathMessage { }
public class MessagingSubscriberExample : MonoBehaviour,
IMessagingSubscriber<PlayerSpawnMessage>,
IMessagingSubscriber<PlayerDeathMessage>
{
private void OnEnable()
{
MessagingSystem.Instance.Subscribe<PlayerSpawnMessage>(this);
MessagingSystem.Instance.Subscribe<PlayerDeathMessage>(this);
}
private void OnDisable()
{
MessagingSystem.Instance.Unsubscribe<PlayerSpawnMessage>(this);
MessagingSystem.Instance.Unsubscribe<PlayerDeathMessage>(this);
}
public void OnReceiveMessage(PlayerSpawnMessage message)
{
Debug.Log(message.ToString());
}
public void OnReceiveMessage(PlayerDeathMessage message)
{
Debug.Log("Received Player Death message");
}
}
You can make use of the DefaultMessagingSystem singleton via the Instance property. Nevertheless, I prefer to inject the dependency, so that later on I can mock it and do unit tests, apart from having a different messaging system implementation if it where required.
public class ExampleDispatcherClass : MonoBehaviour
{
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
MessagingSystem.Instance.Dispatch(new PlayerSpawnMessage(3, 5));
MessagingSystem.Instance.Dispatch(new PlayerDeathMessage());
}
}
}
The same as I said before, be sure to have the same instance referenced. Either by injecting it via a constructor/initialize method or making use of the singleton Instance (although less recommended for obvious reasons, unless you are a beginner).
Unit tested with 100% code coverage to be certain the messaging system implementation works properly.