Global scope
GlobalScope is Saneject's static runtime registry for globally accessible Component instances.
Saneject is mostly editor-time DI, but some runtime scenarios still need lookup by type. GlobalScope and RuntimeProxy covers that gap:
BindGlobal<TComponent>()stores resolved components in aScopeat editor-time and registers them at runtime.RuntimeProxycan resolve throughFromGlobalScope()for fast lookup.- You can also call
GlobalScopedirectly as a service locatorService locatorObject or API that lets runtime code request dependencies on demand instead of receiving them through injection. In Saneject docs, this usually refers toGlobalScope. when needed.
What problem it solves
Unity serialization cannot serialize direct references between certain boundaries:
- Scene ↔ other scene
- Scene ↔ prefab assetPrefab assetReusable prefab definition in the Project window.
- Prefab assetPrefab assetReusable prefab definition in the Project window. ↔ other prefab assetPrefab assetReusable prefab definition in the Project window.
GlobalScope gives you a shared runtime lookup point for registered components. You declare those components in a normal Scope, then Saneject registers them GlobalScope before regular Awake() methods run.
This keeps the common workflow explicit:
- Resolve dependency candidatesDependency candidateObject considered during resolution before qualifiers, filters, and assignment determine whether it becomes the final injected dependency. in the editor through bindingsBindingInstruction declared in a
Scopethat tells Saneject what to resolve, how to inject it, and where to search.. - Persist global candidates on the declaring
Scope. - Register those components into
GlobalScopeat runtime startupRuntime startupPhase after entering Play Mode when scopes initialize, global registrations are populated, and runtime proxy references can be swapped to real instances..
Declaring global components with bindings
Use BindGlobal<TConcrete>() inside DeclareBindings():
using Plugins.Saneject.Runtime.Scopes;
public class BootstrapScope : Scope
{
protected override void DeclareBindings()
{
BindGlobal<AudioManager>()
.FromScopeSelf();
}
}
BindGlobal<T>():
- Only accepts
Componenttypes. - Supports normal
From...locator methods andWhere...filters. - Does not support qualifiers (
ToID,ToTarget,ToMember). - Does not support runtime proxy bindingRuntime proxy bindingComponent binding configured with
FromRuntimeProxy()that injects a proxy asset at editor time and swaps it for a real runtime instance during scope initialization. methods.
See Binding and Scope for more information.
Global lifecycle
When you declare BindGlobal<T>(), Saneject handles it in two phases.
Editor phase:
- During injection, Saneject resolves the bindingBindingInstruction declared in a
Scopethat tells Saneject what to resolve, how to inject it, and where to search. like any other component locator. - If multiple candidates are found, the first candidate is selected.
- The selected component is serialized into the declaring
Scope's global components list.
Runtime phase:
Scope.Awake()runs at execution order-10000.- The scopeScope
MonoBehaviourthat declares bindings for a part of your hierarchy. registers serialized global components intoGlobalScope. - The same
Awake()then swaps runtime proxiesRuntime proxyScriptableObjectplaceholder asset (RuntimeProxy<TComponent>) injected into interface members at editor time and swapped to the real instance during scope startup. to real instances. Scope.OnDestroy()unregisters the components that this scopeScopeMonoBehaviourthat declares bindings for a part of your hierarchy. owns.
That order matters: global registrationGlobal registrationEntry added to GlobalScope at runtime, keyed by the component's concrete type and owned by the caller that registered it. runs before proxy swapping, so runtime proxiesRuntime proxyScriptableObject placeholder asset (RuntimeProxy<TComponent>) injected into interface members at editor time and swapped to the real instance during scope startup. configured to resolve from GlobalScope can do that during startup.
For full proxy behavior, see Runtime proxy.
Using global scope with runtime proxy
A common pattern is:
- In one scopeScope
MonoBehaviourthat declares bindings for a part of your hierarchy., declare a global component withBindGlobal<TConcrete>(). - In another scopeScope
MonoBehaviourthat declares bindings for a part of your hierarchy. or contextContextSerialization boundary Saneject uses during injection to decide scope traversal and candidate eligibility., bind an interface throughFromRuntimeProxy().FromGlobalScope().
using Plugins.Saneject.Runtime.Scopes;
public class BootstrapScope : Scope
{
protected override void DeclareBindings()
{
BindGlobal<GameManager>()
.FromScopeSelf();
}
}
using Plugins.Saneject.Runtime.Scopes;
public class HudScope : Scope
{
protected override void DeclareBindings()
{
BindComponent<IGameManager, GameManager>()
.FromRuntimeProxy()
.FromGlobalScope();
}
}
This is usually the intended use of GlobalScope: fast runtime lookup for proxy resolution, not a replacement for normal editor-time injectionEditor-time injectionSaneject workflow that resolves dependencies and writes them into serialized objects in the Unity Editor, before entering Play Mode..
For full proxy behavior, see Runtime proxy.
Using global scope directly as a service locator
If you want to avoid runtime proxiesRuntime proxyScriptableObject placeholder asset (RuntimeProxy<TComponent>) injected into interface members at editor time and swapped to the real instance during scope startup., direct usage is available when you need fast runtime lookup outside the injection pipelineInjection pipelineOrdered stages Saneject runs during injection, from graph build and context filtering through validation, resolution, assignment, and summary logging.:
using Plugins.Saneject.Runtime.Scopes;
using UnityEngine;
public class AudioBootstrap : MonoBehaviour
{
private void Start()
{
if (GlobalScope.TryGetComponent<AudioManager>(out AudioManager audio))
audio.InitializeMixer();
}
}
API overview:
GlobalScope.GetComponent<T>()GlobalScope.TryGetComponent<T>(out T component)GlobalScope.IsRegistered<T>()GlobalScope.RegisterComponent(Component instance, Object caller)GlobalScope.UnregisterComponent(Component instance, Object caller)GlobalScope.Clear()
Use manual RegisterComponent and UnregisterComponent carefully. Most projects should let Scope manage lifecycle through BindGlobal<T>().
Rules and constraints
GlobalScopestoresComponentinstances only.- Only one registration per concrete component type is allowed at a time.
- Lookup is exact-type by key. If
AudioManageris registered,GetComponent<MonoBehaviour>()does not return it. - Generic lookup methods require
T : Component, so interface lookups are not supported. - Global bindingsBindingInstruction declared in a
Scopethat tells Saneject what to resolve, how to inject it, and where to search. are validated for unique type ownership. DuplicateBindGlobal<SameType>()bindingsBindingInstruction declared in aScopethat tells Saneject what to resolve, how to inject it, and where to search. are invalid. - Global bindingBindingInstruction declared in a
Scopethat tells Saneject what to resolve, how to inject it, and where to search. candidate selection still obeys context isolationContext isolationProject setting (UseContextIsolation) that controls whether dependency resolution can cross context boundaries. rules during editor resolution. - Global bindingsBindingInstruction declared in a
Scopethat tells Saneject what to resolve, how to inject it, and where to search. are not used to resolve[Inject]fields or methods directly. They feed runtime registration, not normal bindingBindingInstruction declared in aScopethat tells Saneject what to resolve, how to inject it, and where to search. resolution. - Modifying
GlobalScope(Register,Unregister,Clear) is runtime-only. Calls outside Play Mode are rejected. - Unregister requires ownership: only the same caller object that registered a type can unregister that type.