This package is a port of the Dependency Injection (DI) system from osu!framework to Unity. It provides an attribute-based workflow that maps naturally to the Unity Transform hierarchy while maintaining zero-allocation performance via C# Source Generators.
In Unity, Dependency Injection usually falls into two camps: reflection-based containers (Zenject) which are flexible but slow, or constructor-based containers (VContainer) which are fast but clash with MonoBehaviour.
This framework uses a hierarchy-walking approach. Dependencies are provided by a parent and consumed by children. Because resolution happens during Awake, it works natively with Object.Instantiate without requiring custom wrappers or manual injection calls.
- Source Generated: A Roslyn Source Generator analyzes your classes at compile-time and generates optimized injection code. This eliminates runtime reflection and GC allocations during dependency resolution.
- Hierarchy-Aware: Scoping is defined by your scene's
Transformstructure. Child objects automatically look up the tree to find the nearest provider for a requested type. - R3 Integration: Native support for reactive state.
[Resolved]properties can beReactiveProperty<T>, which are automatically rebound and disposed of when the object is destroyed. - Async Friendly: Inherits the
[BackgroundDependencyLoader]pattern, allowing for safe, multi-threaded initialization.
| Feature | Zenject | VContainer | osu! DI |
|---|---|---|---|
| Resolution Type | Runtime Reflection | Source Generated | Source Generated |
| Injection Style | Attribute / Method | Constructor | Attribute / Method |
| GC Allocations | High | Minimal | Zero (at runtime) |
| Hierarchy Aware | Yes | No | Yes |
| Native Instantiate | No (requires wrapper) | No (requires wrapper) | Yes |
This repository is not currently using CI/CD to build or publish UPM packages. There are no root GitHub Actions or release workflows in this checkout. The core package is a plain Unity Package Manager package stored in a subdirectory:
UnityPackage/->com.osuframework.unity
That means consumers install it directly from a Git URL with a ?path= suffix, or from a local path while developing. There is no npm/OpenUPM/scoped-registry publishing step yet.
| Package | Path | Purpose |
|---|---|---|
com.osuframework.unity |
UnityPackage |
Core hierarchy-aware osu!framework DI adapter for Unity. |
Use the repository URL with a package path. Replace the URL with the actual remote for your fork or upstream repo.
{
"dependencies": {
"com.cysharp.r3": "https://github.com/Cysharp/R3.git?path=src/R3.Unity/Assets/R3.Unity",
"com.osuframework.unity": "https://github.com/jethac/osu-framework-unity-di.git?path=/UnityPackage#v0.1.0-preview.1"
}
}Pinning to a tag or commit is recommended once releases exist:
https://github.com/jethac/osu-framework-unity-di.git?path=/UnityPackage#v0.1.0-preview.1
For local validation or package development, use file dependencies:
{
"dependencies": {
"com.cysharp.r3": "file:B:/_validation_deps/R3/src/R3.Unity/Assets/R3.Unity",
"com.osuframework.unity": "file:B:/osu-framework-unity-di/UnityPackage"
}
}The validation project in this workspace uses that model.
This package requires R3. The simplest UPM path is:
https://github.com/Cysharp/R3.git?path=src/R3.Unity/Assets/R3.Unity
If your project uses NuGetForUnity for the core R3.dll, keep R3.Unity installed through UPM and ensure the required NuGet assemblies are available to Unity. Set Unity's API Compatibility Level to .NET Standard 2.1 if your project is not already configured that way.
After installing the package, open Unity Package Manager, select the package, and import the listed sample:
com.osuframework.unity:Basic Dynamic UI
Hosted CI is deferred for now. Local Unity validation is the source of truth; this workspace has been validated with Unity 6000.4.6f1 using:
& 'C:\Program Files\Unity\Hub\Editor\6000.4.6f1\Editor\Unity.exe' `
-batchmode `
-projectPath B:\OsuDiValidation `
-runTests `
-testPlatform PlayMode `
-testResults B:\OsuDiValidation\Logs\screens-test-results.xml `
-logFile B:\OsuDiValidation\Logs\screens-tests.logThe validation project should mark both packages testable when running package tests:
{
"testables": [
"com.osuframework.unity"
]
}Release publishing is currently tag-based Git UPM consumption. There is no scoped-registry publish step yet.
- Run Unity PlayMode package tests locally against a validation project.
- Release by tagging the repository.
- Consume packages through Git tags using
?path=/UnityPackage#tag. - Optionally add OpenUPM or private scoped-registry publishing later.
Treat Git path installs as the supported distribution mechanism.
Inherit from DependencyNodeBehaviour to provide dependencies to children. Use the [Cached] attribute on fields or properties.
// Note: Classes using DI must be 'partial' for the Source Generator
public partial class GameController : DependencyNodeBehaviour
{
[Cached]
private string version = "1.0.0";
[Cached]
public ReactiveProperty<int> GlobalScore { get; } = new ReactiveProperty<int>(0);
}Inherit from DependencyBehaviour and use [Resolved] for property injection, or [BackgroundDependencyLoader] for method injection.
public partial class ScoreDisplay : DependencyBehaviour
{
// Resolved properties must be private or protected with a setter
[Resolved]
protected ReadOnlyReactiveProperty<int> Score { get; private set; }
// Method injection: parameters are resolved from the hierarchy
[BackgroundDependencyLoader]
private void load(string version)
{
Debug.Log($"Initialized version: {version}");
// Use 'DependenciesDisposable' to track subscriptions for automatic cleanup
Score.Subscribe(v => Debug.Log($"Score: {v}")).AddTo(DependenciesDisposable);
}
}Dependencies are resolved by walking up the tree. You can override a dependency at any level by caching a new value of the same type in a child DependencyNodeBehaviour.
public partial class SubMenu : DependencyNodeBehaviour
{
// This string will be provided to all children of SubMenu,
// overriding any string cached by parent nodes.
[Cached]
private string localContext = "SubMenuContext";
}- Source Generation: At compile-time, the generator creates a partial class for your behaviour that implements
ISourceGeneratedDependencyActivator. - Awake Hook:
DependencyBehaviour.Awakeis called. - Hierarchy Walk: It calls
transform.GetParentNode(), walking uptransform.parentuntil it finds anIDependencyNode. - Resolution: The generated code fetches the required types from the parent's
DependencyContainerand assigns them to your fields. - R3 Rebinding: If the type is an
ReactiveProperty, the system creates a two-way bound copy and adds it to aCompositeDisposable, which is cleared inOnDestroy.
Licensed under the MIT License.
This project is a port of the osu.Framework.Allocation system.
Original architecture and source generation by ppy Pty Ltd.
Copyright (c) 2024 ppy Pty Ltd contact@ppy.sh. See LICENSE for full details.