-
Notifications
You must be signed in to change notification settings - Fork 0
Integrations Zenject
Zenject (also known as Extenject) is a dependency injection framework for Unity with extensive container, sub-container, and factory support. DxMessaging integrates with Zenject, allowing you to:
-
Inject
IMessageBusas a singleton dependency in any class - Use DI for construction + DxMessaging for events (best of both worlds)
- Create per-scope message buses for scene or gameplay isolation
- Bridge to SignalBus for gradual migration from Zenject Signals
Why combine DI + Messaging? Use constructor injection for service dependencies (repositories, managers) and messaging for reactive events (damage taken, item collected), combining both approaches.
- DxMessaging installed via UPM
- Zenject/Extenject installed (source or UPM)
Create a DxMessagingInstaller to bind the message bus to your Zenject container:
using DxMessaging.Core.MessageBus;
using Zenject;
public sealed class DxMessagingInstaller : MonoInstaller
{
public override void InstallBindings()
{
// Bind MessageBus as a singleton and expose IMessageBus interface
Container.BindInterfacesAndSelfTo<MessageBus>().AsSingle();
// Optional: Enable automatic IMessageRegistrationBuilder binding
// Requires ZENJECT_PRESENT define (auto-added by DxMessaging when Zenject detected)
#if ZENJECT_PRESENT
Container.RegisterMessageRegistrationBuilder();
#endif
}
}- Select (or create) your
ProjectContextprefab - Add
DxMessagingInstalleras a MonoInstaller - Save the prefab
Use IMessageRegistrationBuilder to create message handlers in non-MonoBehaviour classes:
using DxMessaging.Core.MessageBus;
using DxMessaging.Core.Attributes;
using Zenject;
// Define a message
[DxUntargetedMessage]
[DxAutoConstructor]
public readonly partial struct PlayerSpawned
{
public readonly int playerId;
}
// Service that listens to messages
public sealed class PlayerController : IInitializable, IDisposable
{
private readonly MessageRegistrationLease _lease;
// Builder is injected automatically when using the installer
public PlayerController(IMessageRegistrationBuilder registrationBuilder)
{
var options = new MessageRegistrationBuildOptions
{
Configure = token =>
{
_ = token.RegisterUntargeted<PlayerSpawned>(OnPlayerSpawned);
}
};
_lease = registrationBuilder.Build(options);
}
public void Initialize()
{
_lease.Activate(); // Start listening when container initializes
}
public void Dispose()
{
_lease.Dispose(); // Clean up when container disposes
}
private static void OnPlayerSpawned(ref PlayerSpawned message)
{
UnityEngine.Debug.Log($"Player {message.playerId} spawned!");
}
}public sealed class GameInstaller : MonoInstaller
{
public override void InstallBindings()
{
Container.BindInterfacesAndSelfTo<PlayerController>().AsSingle();
}
}If you have existing MessageAwareComponent scripts, you can inject the container-managed bus into them:
using DxMessaging.Core.MessageBus;
using DxMessaging.Unity;
using UnityEngine;
using Zenject;
[RequireComponent(typeof(MessagingComponent))]
public sealed class MessagingComponentConfigurator : MonoBehaviour
{
[Inject]
private IMessageBus _messageBus;
private void Awake()
{
MessagingComponent component = GetComponent<MessagingComponent>();
component.Configure(_messageBus, MessageBusRebindMode.RebindActive);
}
}- Add
MessagingComponentConfiguratoralongside anyMessagingComponentin your prefabs - Zenject will inject the bus before
RegisterMessageHandlersis called - Your message handlers now use the container-managed bus
Alternative approach: Extend MessageAwareComponent and override Awake:
public class ZenjectAwareComponent : MessageAwareComponent
{
[Inject]
private IMessageBus _messageBus;
protected override void Awake()
{
Configure(_messageBus, MessageBusRebindMode.RebindActive);
base.Awake();
}
}For simple cases, inject IMessageBus and emit messages directly:
public sealed class GameInitializer : IInitializable
{
private readonly IMessageBus _messageBus;
public GameInitializer(IMessageBus messageBus)
{
_messageBus = messageBus;
}
public void Initialize()
{
var message = new GameStarted();
_messageBus.EmitUntargeted(ref message);
}
}If you're gradually migrating from Zenject Signals to DxMessaging, you can create a bridge:
using DxMessaging.Core;
using DxMessaging.Core.MessageBus;
using System;
using Zenject;
[DxUntargetedMessage]
[DxAutoConstructor]
public readonly partial struct SceneTransition
{
public readonly string sceneName;
}
public sealed class DxToSignalBridge : IInitializable, IDisposable
{
private readonly IMessageBus _messageBus;
private readonly SignalBus _signalBus;
private MessageRegistrationToken _token;
public DxToSignalBridge(IMessageBus messageBus, SignalBus signalBus)
{
_messageBus = messageBus;
_signalBus = signalBus;
}
public void Initialize()
{
// Create a handler to listen to DxMessaging events
var handler = new MessageHandler(new InstanceId(0), _messageBus)
{
active = true
};
_token = MessageRegistrationToken.Create(handler, _messageBus);
// Bridge DxMessaging -> Zenject Signals
_ = _token.RegisterUntargeted<SceneTransition>(OnSceneTransition);
_token.Enable();
}
public void Dispose()
{
_token?.Disable();
}
private void OnSceneTransition(ref SceneTransition message)
{
// Forward to SignalBus for legacy consumers
_signalBus.Fire(message);
}
}public override void InstallBindings()
{
Container.BindInterfacesAndSelfTo<DxToSignalBridge>().AsSingle();
Container.DeclareSignal<SceneTransition>();
}using DxMessaging.Core.MessageBus;
using Zenject;
using NUnit.Framework;
[TestFixture]
public class GameInitializerTests : ZenjectUnitTestFixture
{
[Test]
public void Initialize_EmitsGameStarted()
{
// Arrange
var bus = new MessageBus();
Container.Bind<IMessageBus>().FromInstance(bus).AsSingle();
Container.BindInterfacesAndSelfTo<GameInitializer>().AsSingle();
bool messageReceived = false;
var handler = new MessageHandler(new InstanceId(1), bus) { active = true };
var token = MessageRegistrationToken.Create(handler, bus);
_ = token.RegisterUntargeted<GameStarted>(ref msg => messageReceived = true);
token.Enable();
// Act
var initializer = Container.Resolve<GameInitializer>();
initializer.Initialize();
// Assert
Assert.IsTrue(messageReceived);
}
}- Install DxMessaging and Zenject/Extenject
- Create
DxMessagingInstallerwithContainer.BindInterfacesAndSelfTo<MessageBus>() - Add installer to your
ProjectContext - Add
#if ZENJECT_PRESENTcheck and callContainer.RegisterMessageRegistrationBuilder()
- Use
IMessageRegistrationBuilderin plain classes withIInitializable/IDisposable - Add
MessagingComponentConfiguratorto prefabs withMessagingComponent - Replace
MessageHandler.MessageBusreferences with injectedIMessageBus - Consider bridging to SignalBus if migrating from Zenject Signals
- Inject
IMessageBusin tests usingFromInstance(new MessageBus()) - Verify messages flow through the container-provided bus
- Vcontainer -- Lightweight alternative to Zenject
- Reflex -- Minimal DI framework
- ..-Getting-Started -- Browse all docs
- Getting-Started-Overview
- Getting-Started-Getting-Started
- Getting-Started-Install
- Getting-Started-Quick-Start
- Getting-Started-Visual-Guide
- Concepts-Message-Types
- Concepts-Listening-Patterns
- Concepts-Targeting-And-Context
- Concepts-Interceptors-And-Ordering
- Guides-Patterns
- Guides-Unity-Integration
- Guides-Testing
- Guides-Diagnostics
- Guides-Advanced
- Guides-Migration-Guide
- Advanced-Emit-Shorthands
- Advanced-Message-Bus-Providers
- Advanced-Runtime-Configuration
- Advanced-String-Messages
- Reference-Reference
- Reference-Quick-Reference
- Reference-Helpers
- Reference-Faq
- Reference-Glossary
- Reference-Troubleshooting
- Reference-Compatibility
Links