7.3 KiB
Editor Lifecycle Bootstrap - Quality of Life Improvement
Date: November 5, 2025
Feature: Editor-Only Lifecycle Orchestration
Status: ✅ Implemented
Problem Statement
When playing a scene directly from the Unity Editor (pressing Play without going through the bootstrap scene), the managed lifecycle system wasn't being fully triggered:
What Was Happening
- ✅ CustomBoot.Initialise() runs (
[RuntimeInitializeOnLoadMethod]) - ✅ LifecycleManager gets created
- ✅ Components' Awake() runs, they register with LifecycleManager
- ✅ OnBootCompletionTriggered() broadcasts
OnManagedAwake()to all components - ❌ BroadcastSceneReady() NEVER CALLED for the initial scene
- ❌ Components never receive their
OnSceneReady()callback
Why This Happened
The OnSceneReady lifecycle event is normally triggered by SceneManagerService.SwitchSceneAsync():
// PHASE 8: Begin scene loading mode
LifecycleManager.Instance?.BeginSceneLoad(newSceneName);
// PHASE 9: Load new gameplay scene
await LoadSceneAsync(newSceneName, progress);
// PHASE 10: Broadcast scene ready
LifecycleManager.Instance?.BroadcastSceneReady(newSceneName);
But when you press Play directly in a scene:
- The scene is already loaded by Unity
- SceneManagerService doesn't orchestrate this initial load
- BroadcastSceneReady is never called
This meant components couldn't properly initialize scene-specific logic in OnSceneReady().
Solution: EditorLifecycleBootstrap
Created an editor-only script that detects when playing directly from a scene and ensures the lifecycle is properly orchestrated.
Implementation Details
File: Assets/Editor/Lifecycle/EditorLifecycleBootstrap.cs
Key Features:
- Automatic Detection: Uses
[InitializeOnLoad]to run in editor - Play Mode Hook: Subscribes to
EditorApplication.playModeStateChanged - Boot Completion Wait: Polls until
CustomBoot.Initialisedis true - Scene Ready Trigger: Broadcasts
OnSceneReadyfor the active scene - One-Time Execution: Only triggers once per play session
- Bootstrap Scene Skip: Ignores the Bootstrap scene (doesn't need OnSceneReady)
How It Works
User Presses Play in Scene "AppleHillsOverworld"
↓
PlayModeStateChange.EnteredPlayMode
↓
EditorLifecycleBootstrap starts polling
↓
Wait for CustomBoot.Initialised == true
↓
Get active scene (AppleHillsOverworld)
↓
Call LifecycleManager.Instance.BroadcastSceneReady("AppleHillsOverworld")
↓
All components in scene receive OnSceneReady() callback ✅
Code Flow
[InitializeOnLoad]
public static class EditorLifecycleBootstrap
{
static EditorLifecycleBootstrap()
{
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
}
private static void OnPlayModeStateChanged(PlayModeStateChange state)
{
if (state == PlayModeStateChange.EnteredPlayMode)
{
// Start polling for boot completion
EditorApplication.update += WaitForBootAndTriggerSceneReady;
}
}
private static void WaitForBootAndTriggerSceneReady()
{
// Wait for boot to complete
if (!Bootstrap.CustomBoot.Initialised)
return;
// Get active scene
Scene activeScene = SceneManager.GetActiveScene();
// Trigger OnSceneReady for the initial scene
LifecycleManager.Instance.BroadcastSceneReady(activeScene.name);
}
}
Benefits
For Developers
- Consistent Lifecycle: Same lifecycle behavior whether you play from Bootstrap or directly from a scene
- No Manual Setup: Automatic - no need to remember to call anything
- Editor-Only: Zero overhead in builds
- Debugging Made Easy: Can test any scene directly without worrying about lifecycle issues
For Components
Components can now reliably use OnSceneReady() for scene-specific initialization:
public class LevelSwitch : ManagedBehaviour
{
protected override void OnSceneReady()
{
Debug.Log($"Scene ready: {gameObject.scene.name}");
// This now works when playing directly from editor! ✅
}
}
Usage
No action required! The system works automatically:
- Open any gameplay scene in Unity
- Press Play
- Components receive their full lifecycle:
OnManagedAwake()✅OnSceneReady()✅ (now works!)
Expected Logs
When playing "AppleHillsOverworld" scene directly:
[CustomBoot] Boot initialized
[LifecycleManager] Instance created
[LifecycleManager] Broadcasting ManagedAwake to 15 components
[LifecycleManager] === Boot Completion Triggered ===
[EditorLifecycleBootstrap] Triggering OnSceneReady for initial scene: AppleHillsOverworld
[LifecycleManager] Broadcasting SceneReady for scene: AppleHillsOverworld
[LevelSwitch] OnSceneReady called for CementFactory
Technical Considerations
Why Not Use RuntimeInitializeOnLoadMethod?
[RuntimeInitializeOnLoadMethod] runs too early - before CustomBoot completes. We need to wait for the full bootstrap to finish before triggering OnSceneReady.
Why EditorApplication.update?
Unity's EditorApplication.update provides a simple polling mechanism to wait for boot completion. It's a lightweight solution for editor-only code.
Why Check for Bootstrap Scene?
The Bootstrap scene is a special infrastructure scene that doesn't contain gameplay components. It doesn't need OnSceneReady, and components there shouldn't expect it.
Thread Safety
All Unity API calls and lifecycle broadcasts happen on the main thread via EditorApplication.update, ensuring thread safety.
Compatibility
- ✅ Works with existing lifecycle system
- ✅ No changes to runtime code
- ✅ No impact on builds (editor-only)
- ✅ Compatible with scene manager service
- ✅ Safe for DontDestroyOnLoad objects
Testing
Test Case 1: Direct Play from Gameplay Scene
- Open
AppleHillsOverworldscene - Press Play
- Verify components log both OnManagedAwake and OnSceneReady
Test Case 2: Play from Bootstrap
- Open
Bootstrapscene - Press Play
- Verify normal boot flow works (should NOT trigger editor bootstrap)
Test Case 3: Scene Transitions
- Play from any scene
- Use LevelSwitch to change scenes
- Verify SceneManagerService still orchestrates transitions normally
Future Enhancements
Potential improvements if needed:
- Configuration: Add developer settings to enable/disable editor lifecycle
- Multi-Scene Support: Handle multiple loaded scenes in editor
- Delayed Trigger: Option to delay OnSceneReady by frames for complex setups
- Debug Visualization: Editor window showing lifecycle state
Related Files
- Implementation:
Assets/Editor/Lifecycle/EditorLifecycleBootstrap.cs - Core System:
Assets/Scripts/Core/Lifecycle/LifecycleManager.cs - Bootstrap:
Assets/Scripts/Bootstrap/CustomBoot.cs - Scene Management:
Assets/Scripts/Core/SceneManagerService.cs
Summary
The EditorLifecycleBootstrap provides a seamless quality-of-life improvement for developers working with the managed lifecycle system. It ensures that playing scenes directly from the Unity Editor provides the same consistent lifecycle orchestration as the production scene flow, making development and debugging significantly easier.