# Scene Management Event Orchestration - Trimming Analysis **Date:** November 4, 2025 **Context:** Post-lifecycle migration - all components now use lifecycle hooks --- ## Executive Summary After migrating components to use ManagedBehaviour lifecycle hooks, **SceneManagerService events can be significantly trimmed**. Most components no longer need these events since they use lifecycle hooks instead. --- ## Current Event Usage Analysis ### SceneManagerService Events (6 total) | Event | Current Subscribers | Can Be Removed? | Notes | |-------|-------------------|-----------------|-------| | **SceneLoadStarted** | 1 (LoadingScreen) | ⚠️ **KEEP** | Loading screen needs this | | **SceneLoadProgress** | 0 | ✅ **REMOVE** | No subscribers | | **SceneLoadCompleted** | 2 (LoadingScreen, PauseMenu) | ⚠️ **KEEP** | Still needed | | **SceneUnloadStarted** | 0 | ✅ **REMOVE** | No subscribers | | **SceneUnloadProgress** | 0 | ✅ **REMOVE** | No subscribers | | **SceneUnloadCompleted** | 0 | ✅ **REMOVE** | No subscribers | --- ## Detailed Event Analysis ### 1. SceneLoadStarted ⚠️ **KEEP** **Current Usage:** ```csharp // SceneManagerService.cs line 102 SceneLoadStarted += _ => _loadingScreen.ShowLoadingScreen(() => GetAggregateLoadProgress()); ``` **Why Keep:** - LoadingScreenController needs to know when loading begins - Shows loading screen with progress callback - No lifecycle equivalent (this is orchestration, not component lifecycle) **Action:** ✅ **KEEP** --- ### 2. SceneLoadProgress ✅ **REMOVE** **Current Usage:** None - no subscribers found **Why Remove:** - Not used anywhere in codebase - Progress is reported via IProgress parameter in async methods - Loading screen gets progress via callback, not event **Action:** ✅ **REMOVE** - Dead code --- ### 3. SceneLoadCompleted ⚠️ **KEEP (but maybe simplify)** **Current Usage:** ```csharp // SceneManagerService.cs line 103 SceneLoadCompleted += _ => _loadingScreen.HideLoadingScreen(); // PauseMenu.cs line 45 SceneManagerService.Instance.SceneLoadCompleted += SetPauseMenuByLevel; ``` **Analysis:** - **LoadingScreen subscriber:** Internal orchestration - keeps loading screen hidden until scene ready - **PauseMenu subscriber:** Legitimate use case - needs to react to EVERY scene load to set visibility **Why PauseMenu Can't Use Lifecycle:** - OnSceneReady() fires only for the scene PauseMenu is IN - PauseMenu is in BootstrapScene (persistent) - Needs to know about OTHER scenes loading to update visibility - Event subscription is correct pattern here **Action:** ⚠️ **KEEP** - Still has legitimate uses --- ### 4. SceneUnloadStarted ✅ **REMOVE** **Current Usage:** None - no subscribers found **Why Remove:** - Components now use OnSceneUnloading() lifecycle hook - LifecycleManager.BroadcastSceneUnloading() replaces this - Event is fired but nobody listens **Action:** ✅ **REMOVE** - Dead code --- ### 5. SceneUnloadProgress ✅ **REMOVE** **Current Usage:** None - no subscribers found **Why Remove:** - Not used anywhere - Progress reported via IProgress parameter **Action:** ✅ **REMOVE** - Dead code --- ### 6. SceneUnloadCompleted ✅ **REMOVE** **Current Usage:** None - no subscribers found **Why Remove:** - Not used anywhere - Components use OnSceneUnloading() before unload - No need to know AFTER unload completes **Action:** ✅ **REMOVE** - Dead code --- ## Trimming Recommendations ### Option A: Aggressive Trimming (Recommended) **Remove these events entirely:** - ❌ SceneLoadProgress - ❌ SceneUnloadStarted - ❌ SceneUnloadProgress - ❌ SceneUnloadCompleted **Keep these events:** - ✅ SceneLoadStarted (loading screen orchestration) - ✅ SceneLoadCompleted (loading screen + PauseMenu cross-scene awareness) **Result:** - **4 events removed** (67% reduction) - **2 events kept** (essential for orchestration) - Clean separation: Lifecycle hooks for components, events for orchestration --- ### Option B: Conservative Approach **Remove only provably unused:** - ❌ SceneLoadProgress - ❌ SceneUnloadProgress **Mark as deprecated but keep:** - ⚠️ SceneUnloadStarted (deprecated - use OnSceneUnloading) - ⚠️ SceneUnloadCompleted (deprecated - use lifecycle) **Result:** - **2 events removed** (33% reduction) - **4 events kept** (backward compatibility) - Gradual migration path --- ### Option C: Nuclear Option (Not Recommended) **Remove ALL events, replace with callbacks:** ```csharp // Instead of events, use callbacks in SwitchSceneAsync public async Task SwitchSceneAsync( string newSceneName, IProgress progress = null, Action onLoadStarted = null, Action onLoadCompleted = null ) ``` **Why NOT Recommended:** - Breaks existing loading screen integration - Removes flexibility - Makes simple orchestration harder --- ## Recommended Implementation ### Step 1: Remove Dead Events **File:** `SceneManagerService.cs` **Remove these declarations:** ```csharp // REMOVE: public event Action SceneLoadProgress; public event Action SceneUnloadStarted; public event Action SceneUnloadProgress; public event Action SceneUnloadCompleted; ``` **Remove these invocations:** ```csharp // REMOVE from LoadSceneAsync: SceneLoadProgress?.Invoke(sceneName, op.progress); // REMOVE from UnloadSceneAsync: SceneUnloadStarted?.Invoke(sceneName); SceneUnloadProgress?.Invoke(sceneName, op.progress); SceneUnloadCompleted?.Invoke(sceneName); ``` --- ### Step 2: Simplify Remaining Events **Keep these (minimal set):** ```csharp /// /// Fired when a scene starts loading. /// Used by loading screen orchestration. /// public event Action SceneLoadStarted; /// /// Fired when a scene finishes loading. /// Used by loading screen orchestration and cross-scene components (e.g., PauseMenu). /// For component initialization, use OnSceneReady() lifecycle hook instead. /// public event Action SceneLoadCompleted; ``` --- ### Step 3: Update Documentation **Add to SceneManagerService class documentation:** ```csharp /// /// Singleton service for loading and unloading Unity scenes asynchronously. /// /// EVENTS vs LIFECYCLE HOOKS: /// - Events (SceneLoadStarted/Completed): For orchestration and cross-scene awareness /// - Lifecycle hooks (OnSceneReady, OnSceneUnloading): For component-level scene initialization /// /// Most components should use lifecycle hooks, not event subscriptions. /// ``` --- ## Impact Analysis ### Before Trimming ```csharp public class SceneManagerService : ManagedBehaviour { // 6 events public event Action SceneLoadStarted; public event Action SceneLoadProgress; // ← UNUSED public event Action SceneLoadCompleted; public event Action SceneUnloadStarted; // ← UNUSED public event Action SceneUnloadProgress; // ← UNUSED public event Action SceneUnloadCompleted; // ← UNUSED } ``` ### After Trimming ```csharp public class SceneManagerService : ManagedBehaviour { // 2 events (essential orchestration only) public event Action SceneLoadStarted; public event Action SceneLoadCompleted; } ``` ### Metrics - **Events removed:** 4 out of 6 (67%) - **Event invocations removed:** ~8 call sites - **Lines of code removed:** ~15-20 lines - **Cognitive complexity:** Reduced significantly - **Maintenance burden:** Lower --- ## Migration Path for Future Components ### ❌ DON'T: Subscribe to scene events ```csharp public class MyComponent : ManagedBehaviour { private void Start() { SceneManagerService.Instance.SceneLoadCompleted += OnSceneLoaded; } } ``` ### ✅ DO: Use lifecycle hooks ```csharp public class MyComponent : ManagedBehaviour { protected override void OnSceneReady() { // Scene is ready, do initialization } } ``` ### ✅ EXCEPTION: Cross-scene awareness ```csharp // Only if component needs to know about OTHER scenes loading // (e.g., PauseMenu in BootstrapScene reacting to gameplay scenes) public class CrossSceneComponent : ManagedBehaviour { protected override void OnSceneReady() { SceneManagerService.Instance.SceneLoadCompleted += OnOtherSceneLoaded; } private void OnOtherSceneLoaded(string sceneName) { // React to OTHER scenes loading } } ``` --- ## Code Quality Improvements ### Before (Complex Event Mesh) ``` Component A ──┐ Component B ──┼──→ SceneLoadProgress (unused) Component C ──┘ Component D ──┐ Component E ──┼──→ SceneUnloadStarted (unused) Component F ──┘ Component G ──┐ Component H ──┼──→ SceneUnloadProgress (unused) Component I ──┘ Component J ──┐ Component K ──┼──→ SceneUnloadCompleted (unused) Component L ──┘ ``` ### After (Clean Separation) ``` LoadingScreen ──→ SceneLoadStarted ✓ (orchestration) LoadingScreen ──→ SceneLoadCompleted ✓ (orchestration) PauseMenu ─────→ SceneLoadCompleted ✓ (cross-scene) All other components → Use lifecycle hooks (OnSceneReady, OnSceneUnloading) ``` --- ## Testing Checklist After implementing trimming: - [ ] Verify loading screen shows/hides correctly - [ ] Verify PauseMenu visibility updates per scene - [ ] Test scene transitions work smoothly - [ ] Check no compilation errors - [ ] Grep for removed event names (ensure no orphaned subscribers) - [ ] Run full playthrough --- ## Recommendation: **Option A (Aggressive Trimming)** **Rationale:** 1. ✅ 67% reduction in events (4 removed) 2. ✅ No breaking changes (removed events had no subscribers) 3. ✅ Clearer architecture (events for orchestration, lifecycle for components) 4. ✅ Easier to maintain going forward 5. ✅ Matches lifecycle system philosophy **Estimated Time:** 15-20 minutes **Risk Level:** 🟢 **LOW** - Removed events have zero subscribers --- ## Next Steps 1. **Remove dead events** from SceneManagerService (10 min) 2. **Update documentation** in SceneManagerService (5 min) 3. **Grep search** to verify no orphaned subscribers (2 min) 4. **Test scene transitions** (5 min) **Total: ~22 minutes** --- **Analysis Complete** **Recommendation:** Proceed with aggressive trimming (Option A)