Files
AppleHillsProduction/docs/levelswitch_onsceneready_fix.md
2025-11-07 13:53:11 +01:00

115 lines
4.1 KiB
Markdown

# OnSceneReady Not Called for LevelSwitch - Root Cause & Fix
## Problem Identified from Logs
```
[LevelSwitch] Awake called for CementFactory in scene AppleHillsOverworld
[LevelSwitch] OnManagedAwake called for CementFactory
```
✅ Awake() called
✅ OnManagedAwake() called
❌ OnSceneReady() NEVER called
## Root Cause Analysis
### The Timing Issue
Looking at the stack trace, `OnManagedAwake()` is being called **during registration** at LifecycleManager line 125, which is the "late registration" code path (boot already complete).
**The sequence that breaks OnSceneReady:**
1. **Phase 8** in `SceneManagerService.SwitchSceneAsync()`:
```csharp
await LoadSceneAsync(newSceneName, progress);
```
- Unity loads the scene
- LevelSwitch.Awake() runs
- LevelSwitch calls base.Awake()
- ManagedBehaviour.Awake() calls LifecycleManager.Register()
- LifecycleManager checks: `if (currentSceneReady == sceneName)` → **FALSE** (not set yet!)
- OnSceneReady() NOT called
2. **Phase 9** in `SceneManagerService.SwitchSceneAsync()`:
```csharp
LifecycleManager.Instance?.BroadcastSceneReady(newSceneName);
```
- Sets `currentSceneReady = sceneName`
- Broadcasts to all components in sceneReadyList
- But LevelSwitch was already checked in step 1, so it's skipped!
### The Gap
Between when `LoadSceneAsync()` completes (scene loaded, Awake() called) and when `BroadcastSceneReady()` is called, there's a timing gap where:
- Components register with LifecycleManager
- `currentSceneReady` is NOT yet set to the new scene name
- Late registration check fails: `currentSceneReady == sceneName` → false
- Components miss their OnSceneReady() call
## The Fix
Modified `LifecycleManager.Register()` to check if a scene is **actually loaded** via Unity's SceneManager, not just relying on `currentSceneReady`:
```csharp
// If this scene is already ready, call OnSceneReady immediately
// Check both currentSceneReady AND if the Unity scene is actually loaded
// (during scene loading, components Awake before BroadcastSceneReady is called)
bool sceneIsReady = currentSceneReady == sceneName;
// Also check if this is happening during boot and the scene is the active scene
// This handles components that register during initial scene load
if (!sceneIsReady && isBootComplete && sceneName != "DontDestroyOnLoad")
{
var scene = UnityEngine.SceneManagement.SceneManager.GetSceneByName(sceneName);
sceneIsReady = scene.isLoaded;
}
if (sceneIsReady)
{
LogDebug($"Late registration: Calling OnSceneReady immediately for {component.gameObject.name}");
component.InvokeSceneReady();
}
```
### Why This Works
1. Components register during scene load (after Awake())
2. `currentSceneReady` might not be set yet
3. BUT `scene.isLoaded` returns true because Unity has already loaded the scene
4. OnSceneReady() gets called immediately during registration
5. Components get their lifecycle hook even though they register between load and broadcast
## Expected Behavior After Fix
When playing the game, you should now see:
```
[LevelSwitch] Awake called for CementFactory in scene AppleHillsOverworld
[LifecycleManager] Late registration: Calling OnManagedAwake immediately for CementFactory
[LevelSwitch] OnManagedAwake called for CementFactory
[LifecycleManager] Late registration: Calling OnSceneReady immediately for CementFactory
[LevelSwitch] OnSceneReady called for CementFactory ← THIS IS NEW!
```
## Files Modified
1. **LifecycleManager.cs** - Enhanced late registration check to verify Unity scene load status
## Design Insight
This reveals an important timing consideration:
**During scene loading:**
- Unity loads the scene
- All Awake() methods run (including base.Awake() for ManagedBehaviours)
- Components register with LifecycleManager
- `SceneManager.LoadSceneAsync` completes
- **THEN** SceneManagerService calls BroadcastSceneReady()
There's an inherent gap between Unity's scene load completion and our lifecycle broadcast. The fix handles this by checking Unity's actual scene state, not just our tracking variable.
---
**Status**: ✅ FIXED - OnSceneReady will now be called for all in-scene ManagedBehaviours, even during late registration!