170 lines
5.3 KiB
Markdown
170 lines
5.3 KiB
Markdown
|
|
# CRITICAL BUG: BroadcastSceneReady Never Called During Boot
|
||
|
|
|
||
|
|
## Problem Report
|
||
|
|
|
||
|
|
User reported: "I don't have that log in my console" referring to:
|
||
|
|
```csharp
|
||
|
|
LogDebug($"Broadcasting SceneReady for scene: {sceneName}");
|
||
|
|
```
|
||
|
|
|
||
|
|
## Root Cause Analysis
|
||
|
|
|
||
|
|
### The Discovery
|
||
|
|
|
||
|
|
Searched console logs for "Broadcasting" - **ZERO results**!
|
||
|
|
|
||
|
|
This means `BroadcastSceneReady()` was **NEVER called**, which explains why:
|
||
|
|
- ❌ LevelSwitch.OnSceneReady() never called
|
||
|
|
- ❌ PuzzleManager.OnSceneReady() never called (before we fixed it)
|
||
|
|
- ❌ All scene lifecycle hooks completely broken during boot
|
||
|
|
|
||
|
|
### The Investigation
|
||
|
|
|
||
|
|
**Where BroadcastSceneReady SHOULD be called:**
|
||
|
|
1. ✅ SceneManagerService.SwitchSceneAsync() - line 364 - **BUT NOT USED DURING BOOT**
|
||
|
|
2. ❌ BootSceneController.LoadMainScene() - **MISSING!**
|
||
|
|
|
||
|
|
**The Problem Code Path:**
|
||
|
|
|
||
|
|
When you play from StartingScene:
|
||
|
|
```csharp
|
||
|
|
// BootSceneController.LoadMainScene() - line 192
|
||
|
|
var op = SceneManager.LoadSceneAsync(mainSceneName, LoadSceneMode.Additive);
|
||
|
|
// ... waits for scene to load
|
||
|
|
SceneManagerService.Instance.CurrentGameplayScene = mainSceneName;
|
||
|
|
_sceneLoadingProgress = 1f;
|
||
|
|
// ❌ STOPS HERE - NO LIFECYCLE BROADCASTS!
|
||
|
|
```
|
||
|
|
|
||
|
|
**What's missing:**
|
||
|
|
- No call to `LifecycleManager.BroadcastSceneReady()`
|
||
|
|
- No call to `LifecycleManager.BroadcastRestoreRequested()`
|
||
|
|
- Components in the loaded scene never get their lifecycle hooks!
|
||
|
|
|
||
|
|
### Why It Happened
|
||
|
|
|
||
|
|
BootSceneController was implemented BEFORE the lifecycle system was fully integrated. It loads scenes directly using Unity's `SceneManager.LoadSceneAsync()` instead of using `SceneManagerService.SwitchSceneAsync()`, which means it completely bypasses the lifecycle broadcasts.
|
||
|
|
|
||
|
|
**The Broken Flow:**
|
||
|
|
```
|
||
|
|
StartingScene loads
|
||
|
|
↓
|
||
|
|
BootSceneController.OnManagedAwake()
|
||
|
|
↓
|
||
|
|
LoadMainScene()
|
||
|
|
↓
|
||
|
|
SceneManager.LoadSceneAsync("AppleHillsOverworld") ← Direct Unity call
|
||
|
|
↓
|
||
|
|
Scene loads, all Awake() methods run
|
||
|
|
↓
|
||
|
|
LevelSwitch registers with LifecycleManager
|
||
|
|
↓
|
||
|
|
... nothing happens ❌
|
||
|
|
↓
|
||
|
|
NO BroadcastSceneReady() ❌
|
||
|
|
NO OnSceneReady() calls ❌
|
||
|
|
```
|
||
|
|
|
||
|
|
## The Fix
|
||
|
|
|
||
|
|
Added lifecycle broadcasts to BootSceneController after scene loading completes:
|
||
|
|
|
||
|
|
```csharp
|
||
|
|
// Update the current gameplay scene in SceneManagerService
|
||
|
|
SceneManagerService.Instance.CurrentGameplayScene = mainSceneName;
|
||
|
|
|
||
|
|
// Ensure progress is complete
|
||
|
|
_sceneLoadingProgress = 1f;
|
||
|
|
|
||
|
|
// CRITICAL: Broadcast lifecycle events so components get their OnSceneReady callbacks
|
||
|
|
LogDebugMessage($"Broadcasting OnSceneReady for: {mainSceneName}");
|
||
|
|
LifecycleManager.Instance?.BroadcastSceneReady(mainSceneName);
|
||
|
|
|
||
|
|
LogDebugMessage($"Broadcasting OnRestoreRequested for: {mainSceneName}");
|
||
|
|
LifecycleManager.Instance?.BroadcastRestoreRequested(mainSceneName);
|
||
|
|
```
|
||
|
|
|
||
|
|
## The Corrected Flow
|
||
|
|
|
||
|
|
```
|
||
|
|
StartingScene loads
|
||
|
|
↓
|
||
|
|
BootSceneController.OnManagedAwake()
|
||
|
|
↓
|
||
|
|
LoadMainScene()
|
||
|
|
↓
|
||
|
|
SceneManager.LoadSceneAsync("AppleHillsOverworld")
|
||
|
|
↓
|
||
|
|
Scene loads, all Awake() methods run
|
||
|
|
↓
|
||
|
|
LevelSwitch registers with LifecycleManager (late registration)
|
||
|
|
↓
|
||
|
|
✅ BroadcastSceneReady("AppleHillsOverworld") ← NEW!
|
||
|
|
↓
|
||
|
|
✅ LevelSwitch.OnSceneReady() called!
|
||
|
|
↓
|
||
|
|
✅ BroadcastRestoreRequested("AppleHillsOverworld")
|
||
|
|
↓
|
||
|
|
✅ Components can restore save data
|
||
|
|
```
|
||
|
|
|
||
|
|
## Expected Logs After Fix
|
||
|
|
|
||
|
|
When playing from StartingScene, you should now see:
|
||
|
|
|
||
|
|
```
|
||
|
|
[BootSceneController] Loading main menu scene: AppleHillsOverworld
|
||
|
|
[BootSceneController] Broadcasting OnSceneReady for: AppleHillsOverworld
|
||
|
|
[LifecycleManager] Broadcasting SceneReady for scene: AppleHillsOverworld ← THIS WAS MISSING!
|
||
|
|
[LevelSwitch] OnSceneReady called for CementFactory ← NOW WORKS!
|
||
|
|
[LevelSwitch] OnSceneReady called for Quarry
|
||
|
|
[LevelSwitch] OnSceneReady called for Dump
|
||
|
|
[BootSceneController] Broadcasting OnRestoreRequested for: AppleHillsOverworld
|
||
|
|
[LifecycleManager] Broadcasting RestoreRequested for scene: AppleHillsOverworld
|
||
|
|
```
|
||
|
|
|
||
|
|
## Impact
|
||
|
|
|
||
|
|
### Before Fix ❌
|
||
|
|
- Boot scene loading bypassed lifecycle system completely
|
||
|
|
- No OnSceneReady() calls during initial boot
|
||
|
|
- No OnRestoreRequested() calls
|
||
|
|
- Late registration check in LifecycleManager only helped with subsequent scene loads
|
||
|
|
- All scene-specific initialization broken during boot!
|
||
|
|
|
||
|
|
### After Fix ✅
|
||
|
|
- Boot scene loading now properly integrates with lifecycle system
|
||
|
|
- OnSceneReady() called for all components in initial scene
|
||
|
|
- OnRestoreRequested() called for save/load integration
|
||
|
|
- Consistent lifecycle behavior whether loading from boot or switching scenes
|
||
|
|
- Full lifecycle system functional!
|
||
|
|
|
||
|
|
## Files Modified
|
||
|
|
|
||
|
|
1. **BootSceneController.cs** - Added lifecycle broadcasts after scene load
|
||
|
|
|
||
|
|
## Design Lesson
|
||
|
|
|
||
|
|
**ANY code that loads scenes must broadcast lifecycle events!**
|
||
|
|
|
||
|
|
This includes:
|
||
|
|
- ✅ SceneManagerService.SwitchSceneAsync() - already does this
|
||
|
|
- ✅ BootSceneController.LoadMainScene() - NOW does this
|
||
|
|
- ⚠️ Any future scene loading code must also do this!
|
||
|
|
|
||
|
|
The lifecycle broadcasts are NOT automatic - they must be explicitly called after scene loading completes.
|
||
|
|
|
||
|
|
## Related Issues Fixed
|
||
|
|
|
||
|
|
This single fix resolves:
|
||
|
|
1. ✅ LevelSwitch.OnSceneReady() not being called during boot
|
||
|
|
2. ✅ Any component's OnSceneReady() not being called during boot
|
||
|
|
3. ✅ OnRestoreRequested() not being called during boot
|
||
|
|
4. ✅ Save/load integration broken during boot
|
||
|
|
5. ✅ Inconsistent lifecycle behavior between boot and scene switching
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**Status**: ✅ FIXED - Boot scene loading now properly broadcasts lifecycle events!
|
||
|
|
|