RuntimeSingletonRegistry clears its instances from a
[RuntimeInitializeOnLoadMethod(BeforeSceneLoad)], and RuntimeSingleton<T>.ClearInstance destroys
every live instance's GameObject, not just the one it cached:
T[] liveInstances = UnityObjectExtensions.FindObjectsOfTypeShim<T>(true);
for (int i = 0; i < liveInstances.Length; i++)
{
liveInstances[i].StopAllCoroutines();
liveInstances[i].gameObject.Destroy();
}
In a player that is harmless: nothing is alive when BeforeSceneLoad runs. In the Editor, pressing
Play on a scene that already carries a RuntimeSingleton is not harmless — the open scene's
objects are alive at that moment, so the sweep destroys objects it did not create, and the scene runs
without the component it authored.
Measured
Unity 6000.5.2f1, m_EnterPlayModeOptionsEnabled: 1, m_EnterPlayModeOptions: 0.
A level scene with eleven root GameObjects, two of which carry a RuntimeSingleton subclass. In Play
mode exactly those two roots are absent and the other nine are present. Nothing else in the scene
selects on "is a RuntimeSingleton", and FindObjectsByType<T>(FindObjectsInactive.Include) returns
zero for both types for the whole session.
The consequence is not an absence, because Instance fabricates one:
| moment |
the LevelBounds the game sees |
| first frame |
none |
after any consumer asks Instance |
(x:0, y:0, width:0, height:0) |
The authored value is (0, 0, 76.8, 43.2). The consumer downstream clamps a camera to the centre of
that rectangle, so the camera left the level and held on the world origin — 62.56 world units in one
move, in a take where nothing else moved.
Why this is worth fixing rather than documenting
Every symptom is silent. Instance cannot fail, the fabricated object answers every call, and the
authored values it replaces are defaults that look plausible. A consumer written the obvious way has
no way to tell which one it is holding.
Suggested shapes
- Have
ClearInstance drop the cached reference and destroy only instances the getter created —
track those, rather than sweeping by type.
- Or skip the sweep when
Application.isPlaying is already true at BeforeSceneLoad, which is the
Editor-only state that makes open-scene objects visible to it.
The clear-the-cache half is what the callback needs; the destroy half is what a test fixture wants,
and the two look worth separating.
Found from Ambiguous-Interactive/IshoBoy#694, where it put both marketing captures of a level on the
world origin for most of their take with every gate still green.
RuntimeSingletonRegistryclears its instances from a[RuntimeInitializeOnLoadMethod(BeforeSceneLoad)], andRuntimeSingleton<T>.ClearInstancedestroysevery live instance's GameObject, not just the one it cached:
In a player that is harmless: nothing is alive when
BeforeSceneLoadruns. In the Editor, pressingPlay on a scene that already carries a
RuntimeSingletonis not harmless — the open scene'sobjects are alive at that moment, so the sweep destroys objects it did not create, and the scene runs
without the component it authored.
Measured
Unity 6000.5.2f1,
m_EnterPlayModeOptionsEnabled: 1,m_EnterPlayModeOptions: 0.A level scene with eleven root GameObjects, two of which carry a
RuntimeSingletonsubclass. In Playmode exactly those two roots are absent and the other nine are present. Nothing else in the scene
selects on "is a RuntimeSingleton", and
FindObjectsByType<T>(FindObjectsInactive.Include)returnszero for both types for the whole session.
The consequence is not an absence, because
Instancefabricates one:LevelBoundsthe game seesInstance(x:0, y:0, width:0, height:0)The authored value is
(0, 0, 76.8, 43.2). The consumer downstream clamps a camera to the centre ofthat rectangle, so the camera left the level and held on the world origin — 62.56 world units in one
move, in a take where nothing else moved.
Why this is worth fixing rather than documenting
Every symptom is silent.
Instancecannot fail, the fabricated object answers every call, and theauthored values it replaces are defaults that look plausible. A consumer written the obvious way has
no way to tell which one it is holding.
Suggested shapes
ClearInstancedrop the cached reference and destroy only instances the getter created —track those, rather than sweeping by type.
Application.isPlayingis already true atBeforeSceneLoad, which is theEditor-only state that makes open-scene objects visible to it.
The clear-the-cache half is what the callback needs; the destroy half is what a test fixture wants,
and the two look worth separating.
Found from Ambiguous-Interactive/IshoBoy#694, where it put both marketing captures of a level on the
world origin for most of their take with every gate still green.