Memory based messaging system akin to ampq style service bus messaging. This enables easy and fast message brokering and reception between scenes via separate assemblies using unity's scriptable objects, mimicing the functionality you'd expect from a Service Bus resource, but without any network tcp/ip layers. This runs specifically in memory to allow multiple styles of Queue/topic subscription based message capabilities. Coming from a microservices background, I decided to see if a simple messaging system, akin to Azure Service Bus, would be possible to establish an efficient and reliable communication bridge between Unity scenes. The result is a decently performant, AMQP style message broker for communicating between Assemblies and scenes without coupling, network layers, or cloud services. Created using 6.3, but it may work with previous 6.xxx versions.
Due to using some v6 specific apis (Awaitable.MainThreadAsync) in particular, so it will not work with < 6 Unity versions without a bit of modification.
Important
AI Usage Restricted: This repository is for human developers only. AI training, scraping, and synthesis are strictly prohibited. See llms.txt for details.
| Concept | Implementation | Behavior |
|---|---|---|
| Topic subscription (fanout) | RoutingMode.Broadcast or explicit subscription: bus.Subscribe<MyMessageType>(handler) |
Dispatched message is received by all receivers that are subscribed |
| Queue subcription (fifo) | RoutingMode.Queue |
Dispatched message is received by one subscriber at a time in the order of their registration |
| RPC / Request-Reply | MessageBus.Request<TReq, TReply>() |
Dispatched request is received and handled by any single subscriber, whom of which can dispatch a response to the origina requester. |
Add via Unity Package Manager/Add package from disk/select package.json.
Or you could also add a reference into Packages/manifest.json:
"com.voidwireinteractive.messaging": "file:../path/to/com.voidwireinteractive.messaging"Assets/Create/Void Wire Interactive/Messaging/Message Bus
using VoidWireInteractive.Messaging.Contracts;
public record class PlayerDied(string PlayerName, Vector3 Position) : IMessage;using VoidWireInteractive.Messaging.Core;
public class DeathScreenController : MonoBehaviourSubscriber<PlayerDied>
{
// Drag your MessageBus asset into the Bus field in the Inspector. it'll be there since this inherits the subscriber mono.
protected override void OnMessageReceived(PlayerDied msg)
{
ShowDeathScreen(msg.PlayerName);
}
}[SerializeField] private MessageBus _bus;
_bus.Publish(new PlayerDied("Hero", transform.position));// Broadcast: All subscribers receive every message
bus.Subscribe<MyMessage>(handler);
bus.Subscribe<MyMessage>(handler, RoutingMode.Broadcast);
// Queue: One subscriber per message, round-robin
bus.Subscribe<WorkItem>(ProcessWork, RoutingMode.Queue);
// RequestReply: One shot responder
bus.Subscribe<QueryHealth>(HandleQuery, RoutingMode.RequestReply);
// Request/Reply from the caller side
var reply = await bus.Request<QueryHealth, HealthResult>(new QueryHealth("Hero"));For non-MonoBehaviour code, or when you need explicit control:
// Subscribe:
var token = bus.Subscribe<PlayerDied>(OnPlayerDied);
// Unsubscribing:
token.Dispose();
bus.Unsubscribe(token);
using var token = bus.Subscribe<PlayerDied>(handler); // auto-disposesPlace a MessageBus asset at Resources/Messaging/DefaultMessageBus. It will be automatically registered as GlobalBus.Default before the first scene loads. Components whose Bus field is left null will use this automatically.
Create additional bus assets for isolated subsystems:
AudioEventBus: Sound effects and musicUIBus: HUD updatesNetworkBus: server event relay (idk just an idea, might work but I dont make network multiplayer games atm)
Each bus has its own channel, its own capacity settings, and its own subscriber registry.
Window/Void Wire Interactive/Messaging/Bus Monitor
During Play Mode, shows subscriber count by type, publish count, drop count, and channel backlog for every bus asset in the project. A non zero Drop count means _channelCapacity needs increasing.
Note
Currently, no dead letter functionality is available, but I may implement this as a sort of fallback measure regardless of drop count being hit.
Runtime/
Contracts/VoidWireInteractive.Messaging.Contracts: IMessage interface only. No Unity deps
Core/VoidWireInteractive.Messaging.Core: MessageBus, BusRouter, MonoBehaviourSubscriber
Editor/VoidWireInteractive.Messaging.Editor: EditorWindow
Samples~/Void Wire Interactive/MessageBusSamples: optional samples you can import via package manager
Your game assemblies reference VoidWireInteractive.Messaging.Contracts for message types and VoidWireInteractive.Messaging.Core for the bus API. They never reference each other.
- Zero allocation on publish:
TryWriteon a bounded channel is a lock-free ring buffer write. - No boxing: messages are
class/recordtypes. storing asIMessageis a reference copy, not a box. - Main-thread dispatch:
Awaitable.MainThreadAsync()uses Unity's internal task pooling which is cheaper thanUnityMainThreadDispatcherpatterns. - Bounded channel: protects against publisher/consumer imbalance. Drop policy and capacity are tunable per-bus in the Inspector.
- Error isolation: one bad subscriber handler cannot stop the router loop or affect other subscribers.