# OnSceneReady() Not Called - Root Cause Analysis & Fix ## Problem Two ManagedBehaviours were not receiving their `OnSceneReady()` callbacks: 1. **PuzzleManager** (bootstrapped singleton in DontDestroyOnLoad) 2. **LevelSwitch** (in-scene component) ## Root Cause ### The Design of OnSceneReady() `LifecycleManager.BroadcastSceneReady(sceneName)` only calls `OnSceneReady()` on components **IN the specific scene being loaded**: ```csharp public void BroadcastSceneReady(string sceneName) { foreach (var component in sceneReadyList) { if (componentScenes.TryGetValue(component, out string compScene) && compScene == sceneName) { component.InvokeSceneReady(); // Only if compScene == sceneName! } } } ``` When a component registers, LifecycleManager tracks which scene it belongs to: ```csharp var sceneName = component.gameObject.scene.name; componentScenes[component] = sceneName; ``` ### PuzzleManager Issue **Registration Log:** ``` [LifecycleManager] Registered PuzzleManager(Clone) (Scene: DontDestroyOnLoad) ``` **The Problem:** - PuzzleManager is in scene "DontDestroyOnLoad" (bootstrapped) - When AppleHillsOverworld loads, `BroadcastSceneReady("AppleHillsOverworld")` is called - Lifecycle manager only broadcasts to components where `compScene == "AppleHillsOverworld"` - PuzzleManager's `compScene == "DontDestroyOnLoad"` ❌ - **Result:** `OnSceneReady()` never called! ### LevelSwitch Issue LevelSwitch should work since it's IN the gameplay scene. However, we need to verify with debug logging to confirm: 1. That it's being registered 2. That the scene name matches 3. That BroadcastSceneReady is being called with the correct scene name ## Solution ### For PuzzleManager (and all bootstrapped singletons) ❌ **Don't use OnSceneReady()** - it only works for components IN the scene being loaded ✅ **Use SceneManagerService.SceneLoadCompleted event** - fires for ALL scene loads **Before (Broken):** ```csharp protected override void OnSceneReady() { // Never called because PuzzleManager is in DontDestroyOnLoad! string sceneName = SceneManager.GetActiveScene().name; LoadPuzzlesForScene(sceneName); } ``` **After (Fixed):** ```csharp protected override void OnManagedAwake() { // Subscribe to scene load events - works for DontDestroyOnLoad components! if (SceneManagerService.Instance != null) { SceneManagerService.Instance.SceneLoadCompleted += OnSceneLoadCompleted; } } private void OnSceneLoadCompleted(string sceneName) { LoadPuzzlesForScene(sceneName); } protected override void OnDestroy() { if (SceneManagerService.Instance != null) { SceneManagerService.Instance.SceneLoadCompleted -= OnSceneLoadCompleted; } } ``` ### For LevelSwitch Added comprehensive debug logging to trace the lifecycle: - Awake call confirmation - Scene name verification - OnManagedAwake call confirmation - OnSceneReady call confirmation This will help identify if the issue is: - Registration not happening - Scene name mismatch - BroadcastSceneReady not being called ## Design Guidelines ### When to use OnSceneReady(): ✅ **Scene-specific components** (components that live IN a gameplay scene) - Level-specific initializers - Scene decorators - In-scene interactables (like LevelSwitch should be) ### When NOT to use OnSceneReady(): ❌ **Bootstrapped singletons** (components in DontDestroyOnLoad) - PuzzleManager - InputManager - Any manager that persists across scenes ### Alternative for bootstrapped components: ✅ Subscribe to `SceneManagerService.SceneLoadCompleted` event - Fires for every scene load - Provides scene name as parameter - Works regardless of component's scene ## Pattern Summary ### Bootstrapped Singleton Pattern: ```csharp public class MyBootstrappedManager : ManagedBehaviour { protected override void OnManagedAwake() { // Subscribe to scene events if (SceneManagerService.Instance != null) { SceneManagerService.Instance.SceneLoadCompleted += OnSceneLoadCompleted; } } private void OnSceneLoadCompleted(string sceneName) { // Handle scene load } protected override void OnDestroy() { // Unsubscribe if (SceneManagerService.Instance != null) { SceneManagerService.Instance.SceneLoadCompleted -= OnSceneLoadCompleted; } base.OnDestroy(); } } ``` ### In-Scene Component Pattern: ```csharp public class MySceneComponent : ManagedBehaviour { protected override void OnSceneReady() { // This WILL be called for in-scene components // Safe to initialize scene-specific stuff here } } ``` ## Files Modified 1. **PuzzleManager.cs** - Removed `OnSceneReady()` override - Added subscription to `SceneLoadCompleted` event in `OnManagedAwake()` - Added `OnSceneLoadCompleted()` callback - Added proper cleanup in `OnDestroy()` 2. **LevelSwitch.cs** - Added comprehensive debug logging - Kept `OnSceneReady()` since it should work for in-scene components - Will verify with logs that it's being called ## Expected Behavior After Fix ### PuzzleManager: ``` [LifecycleManager] Registered PuzzleManager(Clone) (Scene: DontDestroyOnLoad) [PuzzleManager] OnManagedAwake called [SceneManagerService] Scene loaded: AppleHillsOverworld [Puzzles] Scene loaded: AppleHillsOverworld, loading puzzle data ``` ### LevelSwitch: ``` [LevelSwitch] Awake called for LevelSwitch in scene AppleHillsOverworld [LifecycleManager] Registered LevelSwitch (Scene: AppleHillsOverworld) [LifecycleManager] Broadcasting SceneReady for scene: AppleHillsOverworld [LevelSwitch] OnManagedAwake called for LevelSwitch [LevelSwitch] OnSceneReady called for LevelSwitch ``` --- **Status**: ✅ PuzzleManager fixed. LevelSwitch debug logging added for verification.