| name | behaviour-inject |
| description | Guide for using Behaviour Inject (BInject) - a lightweight dependency injection framework for Unity3D |
Behaviour Inject (BInject)
BInject is a performant, lightweight dependency injection framework for Unity3D. This skill provides guidance on how to use it effectively in the project.
Overview
BInject provides:
- Field, property, and method injection via
[Inject] attribute
- Interface binding for abstraction
- Automatic object composition with constructor injection
- Factories for custom object creation
- Event system without manual subscribe/unsubscribe
- Commands that react to events
- Hierarchical contexts for scene-specific dependencies
Core Concepts
1. Context Setup
Create a context early in the game lifecycle (before Injector components awake):
using BehaviourInject;
public class GameInitializer : MonoBehaviour
{
void Awake()
{
Context context = Context.Create();
context.RegisterDependency(new GameSettings());
context.RegisterDependency(new AudioManager());
}
}
2. Injection in MonoBehaviours
[!CAUTION]
REQUIRED: For [Inject] to work in any MonoBehaviour, the Injector component MUST be attached to the same GameObject. The Injector must also have the correct context selected in the Inspector. Without an Injector on the same GameObject, injection via [Inject] attribute will NOT work!
Add the Injector component to GameObjects that need injection. It must be placed before other MonoBehaviours that require dependencies in the component order.
IMPORTANT: Never use the [Inject] attribute on private, protected or internal fields, properties, or methods. BInject must be able to access the member, so apply [Inject] only to public members.
using BehaviourInject;
public class PlayerController : MonoBehaviour
{
[Inject]
public GameSettings Settings { get; private set; }
[Inject]
private AudioManager _audio;
[Inject]
public void Init(GameSettings settings, AudioManager audio)
{
}
}
3. Script Execution Order
For reliable injection, configure execution order:
- Context Creator → First (creates and populates context)
- Injector → Second (performs injection)
- Game Logic → After (dependencies available)
Set in: Edit → Project Settings → Script Execution Order
Registration Methods
Basic Registration
context.RegisterDependency(myInstance);
context.RegisterDependencyAs<MyClass, IMyInterface>(myInstance);
### Type Registration (Autocomposition)
```csharp
context.RegisterType<NetworkManager>();
context.RegisterTypeAs<MockReader, IReader>();
### Factory Registration
```csharp
context.RegisterAsFunction<Connection, ConnectionConfig>(conn => conn.Config);
context.RegisterFactory<Game, GameFactory>();
Autocomposition
BInject automatically creates objects using constructor injection:
public class Connection
{
private readonly Settings _settings;
private readonly Logger _logger;
[Inject]
public Connection(Settings settings, Logger logger)
{
_settings = settings;
_logger = logger;
}
public Connection() { }
}
context.RegisterDependency(new Settings())
.RegisterDependency(new Logger())
.RegisterType<Connection>();
The [Create] Attribute
Use [Create] to instantiate new objects instead of resolving from context:
public class GameManager : MonoBehaviour
{
[Create]
public void Setup(Connection connection)
{
}
[Inject]
public void Init(Settings settings, [Create] Connection connection)
{
}
}
IInstantiator
For runtime object creation with dependency injection:
public class EnemySpawner : MonoBehaviour
{
[Inject]
private IInstantiator _instantiator;
public void SpawnEnemy()
{
Enemy enemy = _instantiator.New<Enemy>();
var config = new EnemyConfig { Damage = 10 };
Enemy customEnemy = _instantiator.New<Enemy>(config);
}
}
Factories
For complex creation logic:
public class GameFactory : DependencyFactory<Game>
{
private Connection _connection;
public GameFactory(Connection connection)
{
_connection = connection;
}
public Game Create()
{
if (_connection.Connected)
return new Game(1, "Online Game");
return new Game(0, "Offline Game");
}
}
context.RegisterFactory<Game, GameFactory>();
public class GameLauncher : MonoBehaviour
{
[Inject]
public Game CurrentGame { get; set; }
[Inject]
public GameFactory Factory { get; set; }
}
Event System
Events are dispatched through DI without manual subscription:
Dispatching Events
public class GameEventSender : MonoBehaviour
{
[Inject]
private IEventDispatcher _dispatcher;
public void OnPlayerDied()
{
_dispatcher.DispatchEvent(new PlayerDiedEvent { PlayerId = 1 });
}
}
Receiving Events
public class UIManager : MonoBehaviour
{
[InjectEvent]
public void OnPlayerDied(PlayerDiedEvent evt)
{
ShowGameOverScreen(evt.PlayerId);
}
[InjectEvent(Inherit = true)]
public void OnGameEvent(IGameEvent evt)
{
}
}
Delegate-based Events
public class ScoreManager : MonoBehaviour
{
[InjectEvent]
public Action<ScoreChangedEvent> OnScoreChanged;
void Start()
{
OnScoreChanged += HandleScoreChanged;
}
void HandleScoreChanged(ScoreChangedEvent evt) { }
}
Commands
Commands execute automatically when specific events occur:
public class SaveGameCommand : ICommand
{
private ISaveService _saveService;
private GameSavedEvent _event;
public SaveGameCommand(ISaveService saveService)
{
_saveService = saveService;
}
[InjectEvent]
public void SetEvent(GameSavedEvent evt)
{
_event = evt;
}
public void Execute()
{
_saveService.Save(_event.SlotId);
}
}
context.RegisterCommand<GameSavedEvent, SaveGameCommand>();
Multiple Contexts
For separate dependency scopes:
Context.Create("game_context");
Context.Create("ui_context");
Context.Create("level_context")
.SetParentContext("game_context");
Set context in Inspector on Injector component.
HierarchyContext
For GameObject-specific dependency scopes:
public class RoomContext : HierarchyContext
{
private Context _context;
public override Context GetContext()
{
if (_context == null)
{
_context = Context.CreateLocal()
.SetParentContext("game_context")
.RegisterDependency(new RoomData());
}
return _context;
}
void OnDestroy()
{
_context.Destroy();
}
}
Enable "Use Hierarchy" on child Injector components.
Context Lifecycle
if (Context.Exists("game_context"))
{
}
context.Destroy();
context.OnContextDestroyed += HandleDestroyed;
Best Practices
- Method Injection - Prefer
[Inject] on methods for explicit dependencies
- Interface Registration - Register as interfaces for testability
- Execution Order - Always configure Script Execution Order
- Context Lifetime - Destroy contexts in
OnDestroy() when using CreateLocal()
- Single Context - Use one context when possible; multiple only for isolation
- Avoid Service Locators - Use
IInstantiator sparingly, prefer constructor injection
Common Patterns
Service Locator Alternative
[Inject]
public GameManager GameManager { get; set; }
Testing with Mocks
context.RegisterTypeAs<NetworkService, INetworkService>();
context.RegisterDependencyAs<MockNetworkService, INetworkService>(new MockNetworkService());
Scene-Specific Dependencies
Context.Create("main")
.RegisterDependency(new AudioManager());
Context.Create("level")
.SetParentContext("main")
.RegisterDependency(new LevelData());
Troubleshooting
| Issue | Solution |
|---|
| Dependencies null in Awake | Check Script Execution Order |
| "Type not registered" | Ensure registration happens before injection |
| Duplicate events | Component registered both as dependency and Injector target |
| Context cycling | Don't set parent to self or descendant |
| Null context | Ensure context created before Injector awakens |
Package Location
The package is located at: Packages/com.sergeysychov.behaviour_inject/
Key files:
Scripts/Context.cs - Main context API
Scripts/Injector.cs - MonoBehaviour injector component
Scripts/Attributes.cs - [Inject], [Create], [InjectEvent]
Scripts/DependencyFactory.cs - Factory base class
Scripts/ICommand.cs - Command interface
Scripts/HierarchyContext.cs - Hierarchy-based context