51 KiB
ManagedBehaviour Lifecycle System - Implementation Guide
Overview
This document provides a step-by-step implementation plan for creating a custom lifecycle management system that extends MonoBehaviour with deterministic, ordered lifecycle hooks. The system integrates with existing bootstrap, scene management, save/load, and game state management infrastructure.
Current System Analysis
Existing Lifecycle Flow
-
Unity Bootstrap Phase
RuntimeInitializeOnLoadMethod(BeforeSplashScreen)→CustomBoot.Initialise()- Loads CustomBootSettings via Addressables
- BootstrapScene loads with initial GameObjects
-
Unity Standard Lifecycle
Awake()- All MonoBehaviours (unordered within frame)OnEnable()- After Awake phaseStart()- After OnEnable phase- Game loop:
Update(),FixedUpdate(),LateUpdate()
-
Custom Bootstrap Integration
- Components register with
BootCompletionService.RegisterInitAction()inAwake() CustomBootcompletes → triggersBootCompletionService.HandleBootCompleted()BootCompletionServiceexecutes registered actions (priority-sorted, 0-1000+)- Components implement
InitializePostBoot()methods
- Components register with
-
Scene Lifecycle Integration
SceneManagerServicemanages additive scene loading/unloading- Components subscribe to
SceneLoadCompletedandSceneUnloadStartedevents SceneManagerService.SwitchSceneAsync()handles scene transitions
-
Save/Load System
SaveLoadManagermaintains registry ofISaveParticipantcomponents- Components call
RegisterParticipant()in post-boot initialization - Components call
UnregisterParticipant()inOnDestroy() - State restoration happens after load or when new participants register
-
Game State Management (GameManager)
- Pause/Resume system with counter-based requests
IPausablecomponent registration (manual Subscribe/Unsubscribe)- Settings initialization (ServiceLocator pattern)
- Developer settings integration
Existing Patterns
- Singleton Pattern: All managers use
_instancefield withInstanceproperty - Two-Phase Init:
Awake()+InitializePostBoot() - Priority-Based Boot: Components specify boot priority (10-100+, lower = earlier)
- Manual Event Management: Subscribe in OnEnable/Start, unsubscribe in OnDestroy
- Inheritance Example:
InteractionActionBaseprovidesprotected virtuallifecycle hooks
Architecture Design
Core Components
1. LifecycleManager (Singleton)
Central orchestrator that:
- Maintains registries of all ManagedBehaviours (separate lists per lifecycle phase)
- Broadcasts lifecycle events in priority-sorted order
- Integrates with BootCompletionService, SceneManagerService, SaveLoadManager, GameManager
- Handles late registration and edge cases
- Provides optional debug/profiling tools
2. ManagedBehaviour (Base Class)
Enhanced MonoBehaviour providing:
- Virtual lifecycle hooks (opt-in via override)
- Automatic registration/unregistration with LifecycleManager
- Priority/ordering support via properties
- Built-in managed event subscription system (auto-cleanup)
- Optional auto-integration with SaveLoadManager and GameManager
3. ManagedEvent System
Helper system for automatic event subscription cleanup:
- Stores event subscriptions internally
- Auto-unsubscribes all events on destruction
- Prevents memory leaks and manual cleanup
Lifecycle Phases
Complete Lifecycle Flow
┌─────────────────────────────────────────────────────────────┐
│ PHASE 1: UNITY ENGINE BOOTSTRAP │
│ - RuntimeInitializeOnLoadMethod (CustomBoot.Initialise) │
│ - BootstrapScene loads │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ PHASE 2: UNITY STANDARD LIFECYCLE │
│ - Unity Awake() [all MonoBehaviours] │
│ └→ ManagedBehaviour.Awake() auto-registers with Manager │
│ - Unity OnEnable() │
│ - Unity Start() │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ PHASE 3: MANAGED AWAKE (Deterministic) │
│ - LifecycleManager.BroadcastManagedAwake() │
│ - Calls OnManagedAwake() in priority order (0→1000) │
│ - Singleton initialization, component setup │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ PHASE 4: BOOT COMPLETION │
│ - CustomBoot completes │
│ - BootCompletionService.HandleBootCompleted() │
│ - LifecycleManager.BroadcastBootComplete() │
│ - Calls OnBootComplete() in priority order │
│ - Settings loaded, services initialized │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ PHASE 5: SCENE READY │
│ - SceneManagerService finishes loading │
│ - LifecycleManager.BroadcastSceneReady() │
│ - Calls OnSceneReady() for all active ManagedBehaviours │
│ - Scene-specific initialization │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ PHASE 6: GAME LOOP (Runtime) │
│ - Unity Update/FixedUpdate/LateUpdate │
│ - Optional: OnManagedUpdate() (priority-ordered) │
│ - Optional: OnManagedFixedUpdate() (priority-ordered) │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ PHASE 7: PAUSE/RESUME (State Change) │
│ - GameManager.RequestPause() / ReleasePause() │
│ - LifecycleManager.BroadcastPause/Resume() │
│ - Calls OnPaused() / OnResumed() for all active │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ PHASE 8: SCENE UNLOADING (Transition) │
│ - SceneManagerService.SwitchSceneAsync() starts │
│ - LifecycleManager.BroadcastSceneUnloading() │
│ - Calls OnSceneUnloading() for scene-specific components │
│ - Cleanup before scene unload │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ PHASE 9: DESTRUCTION │
│ - Unity OnDisable() │
│ - Unity OnDestroy() │
│ └→ ManagedBehaviour.OnDestroy() auto-unregisters │
│ - LifecycleManager.BroadcastManagedDestroy() │
│ - Calls OnManagedDestroy() in reverse priority order │
│ - Auto-cleanup of managed events │
└─────────────────────────────────────────────────────────────┘
Lifecycle Hook Reference
| Hook Name | Priority Order | When Called | Use Case |
|---|---|---|---|
OnManagedAwake() |
Low→High (0→∞) | After Unity Awake, before Start | Deterministic initialization |
OnBootComplete() |
Low→High (0→∞) | After CustomBoot completes | Post-boot initialization (replaces InitializePostBoot) |
OnSceneReady() |
Low→High (0→∞) | After scene fully loaded | Scene-specific setup |
OnManagedUpdate() |
Low→High (0→∞) | Every frame (optional) | Ordered per-frame logic |
OnManagedFixedUpdate() |
Low→High (0→∞) | Every physics frame (optional) | Ordered physics logic |
OnPaused() |
Low→High (0→∞) | When game pauses | Pause state handling |
OnResumed() |
Low→High (0→∞) | When game resumes | Resume state handling |
OnSceneUnloading() |
High→Low (∞→0) | Before scene unloads | Scene cleanup |
OnManagedDestroy() |
High→Low (∞→0) | Before Unity OnDestroy | Guaranteed cleanup |
Implementation Steps
STEP 1: Create Core Enums and Interfaces
File: Assets/Scripts/Core/Lifecycle/LifecycleEnums.cs
Actions:
- Create namespace
Core.Lifecycle - Define
LifecyclePhaseenum with all phases - Define
LifecycleFlagsenum for component opt-in (bitflags) - Add XML documentation for each value
Key Details:
- LifecyclePhase: ManagedAwake, BootComplete, SceneReady, Update, FixedUpdate, Paused, Resumed, SceneUnloading, ManagedDestroy
- LifecycleFlags: None, ManagedAwake, BootComplete, SceneReady, ManagedUpdate, ManagedFixedUpdate, Pause, SceneUnloading, ManagedDestroy, All (bitwise OR of all)
STEP 2: Create ManagedEvent System
File: Assets/Scripts/Core/Lifecycle/ManagedEventSubscription.cs
Actions:
- Create internal struct
ManagedEventSubscriptionto store event delegate and target - Create class
ManagedEventManagerwith:- List of subscriptions per ManagedBehaviour
RegisterEvent<T>(target, action)methodUnregisterAllEvents()method that invokes-=on all stored subscriptions
- Handle both
Actionand genericAction<T>delegates - Use reflection to dynamically unsubscribe
Key Details:
- Store event info as: object target (event owner), Delegate handler, string eventName
- Support common Unity event patterns (Action, Action<T>, UnityEvent, etc.)
- Provide safe null checks before unsubscribing
STEP 3: Create ManagedBehaviour Base Class (Structure)
File: Assets/Scripts/Core/Lifecycle/ManagedBehaviour.cs
Actions:
- Create abstract class
ManagedBehaviour : MonoBehaviour - Add virtual properties for priorities:
ManagedAwakePriority(default 100)BootCompletePriority(default 100)SceneReadyPriority(default 100)UpdatePriority(default 100)DestroyPriority(default 100)
- Add property
ActiveLifecyclePhases(returns LifecycleFlags, default None) - Add property
AutoRegisterSaveParticipant(default false) - Add property
AutoRegisterPausable(default false) - Add property
IsPersistent(default false) - survives scene changes - Add private field
ManagedEventManager _eventManager - Add private bool
_isRegisteredto track registration state
Key Details:
- All priority properties are virtual so subclasses can override
- ActiveLifecyclePhases determines which hooks are called (opt-in)
- IsPersistent flag controls whether component persists across scenes
STEP 4: Implement ManagedBehaviour Lifecycle Hooks (Virtual Methods)
File: Assets/Scripts/Core/Lifecycle/ManagedBehaviour.cs (continued)
Actions:
- Add virtual protected methods (empty by default):
OnManagedAwake()OnBootComplete()OnSceneReady()OnManagedUpdate()OnManagedFixedUpdate()OnPaused()OnResumed()OnSceneUnloading()OnManagedDestroy()
- Add XML documentation for each method
- Add protected helper methods:
RegisterManagedEvent<T>(target, action)- wraps _eventManagerIsLifecyclePhaseActive(LifecyclePhase)- checks flags
Key Details:
- All hooks are
protected virtual void(no return value) - Empty implementations allow opt-in override pattern
- Use
[Conditional("UNITY_EDITOR")]attribute for debug-only hooks if needed
STEP 5: Implement ManagedBehaviour Unity Integration
File: Assets/Scripts/Core/Lifecycle/ManagedBehaviour.cs (continued)
Actions:
- Implement
protected virtual void Awake():- Initialize _eventManager
- Register with LifecycleManager (call
LifecycleManager.Instance.Register(this)) - Set _isRegistered = true
- Implement
protected virtual void OnDestroy():- Unregister from LifecycleManager (call
LifecycleManager.Instance.Unregister(this)) - Call _eventManager.UnregisterAllEvents()
- Auto-unregister from SaveLoadManager and GameManager if applicable
- Set _isRegistered = false
- Unregister from LifecycleManager (call
- Make both methods call base implementation warning (for subclasses)
Key Details:
- Use null checks for LifecycleManager.Instance (may not exist during shutdown)
- Provide clear error logging if registration fails
- Support scenario where subclass overrides Awake/OnDestroy (must call base)
STEP 6: Create LifecycleManager (Structure)
File: Assets/Scripts/Core/Lifecycle/LifecycleManager.cs
Actions:
- Create class
LifecycleManager : MonoBehaviour - Implement singleton pattern (private static _instance, public static Instance)
- Add separate
List<ManagedBehaviour>for each lifecycle phase:_managedAwakeList_bootCompleteList_sceneReadyList_updateList_fixedUpdateList_pauseList_sceneUnloadingList_destroyList
- Add Dictionary<ManagedBehaviour, int> for tracking which lists each component is in (bitmask)
- Add bool flags:
_isBootComplete,_isSceneReady,_isPaused - Add queue for late registrations:
Queue<ManagedBehaviour> _pendingRegistrations
Key Details:
- Each list must be kept sorted by priority
- Use SortedList or List with manual sorting after registration
- Dictionary tracks which phases each component participates in
STEP 7: Implement LifecycleManager Registration System
File: Assets/Scripts/Core/Lifecycle/LifecycleManager.cs (continued)
Actions:
- Implement
public void Register(ManagedBehaviour component):- Check component.ActiveLifecyclePhases flags
- Add to appropriate lists based on flags
- Insert in priority-sorted position
- Handle auto-registration with SaveLoadManager if component is ISaveParticipant && AutoRegisterSaveParticipant
- Handle auto-registration with GameManager if component is IPausable && AutoRegisterPausable
- If boot already complete and component has BootComplete flag, immediately call OnBootComplete
- If scene already ready and component has SceneReady flag, immediately call OnSceneReady
- Implement
public void Unregister(ManagedBehaviour component):- Remove from all lists
- Handle auto-unregistration from SaveLoadManager and GameManager
Key Details:
- Use binary search for insertion point (lists sorted by priority)
- Handle edge case: registration during broadcast (queue and process later)
- Validate component is not null and has valid priorities
STEP 8: Implement LifecycleManager Broadcast System
File: Assets/Scripts/Core/Lifecycle/LifecycleManager.cs (continued)
Actions:
- Implement broadcast methods for each phase:
BroadcastManagedAwake()BroadcastBootComplete()BroadcastSceneReady()BroadcastPause()BroadcastResume()BroadcastSceneUnloading()BroadcastManagedDestroy()
- Each method iterates through corresponding list and calls hook
- Add try-catch around each call with error logging
- Process pending registrations after broadcast completes
- Set state flags (_isBootComplete, _isSceneReady, _isPaused)
Key Details:
- Iterate forward for normal phases (low→high priority)
- Iterate backward for cleanup phases (SceneUnloading, ManagedDestroy)
- Use
forloop with index (not foreach) to handle list modifications - Log phase start/end with timing info (use Stopwatch)
STEP 9: Implement LifecycleManager Update Handlers
File: Assets/Scripts/Core/Lifecycle/LifecycleManager.cs (continued)
Actions:
- Implement
void Update():- Check if _updateList is empty (skip if so)
- Iterate _updateList and call OnManagedUpdate() on each
- Add try-catch with error logging
- Implement
void FixedUpdate():- Check if _fixedUpdateList is empty (skip if so)
- Iterate _fixedUpdateList and call OnManagedFixedUpdate() on each
- Add try-catch with error logging
- Add optimization: disable Update/FixedUpdate when lists are empty
Key Details:
- Use
enabled = falsewhen no components registered for updates - Re-enable when first component registers
- Consider pooling/caching for performance
STEP 10: Integrate LifecycleManager with Bootstrap
File: Assets/Scripts/Core/Lifecycle/LifecycleManager.cs (continued)
Actions:
- In
Awake():- Set singleton instance
- Subscribe to BootCompletionService.OnBootComplete
- Set up initial state
- Implement handler for
BootCompletionService.OnBootComplete:- Set _isBootComplete = true
- Call BroadcastManagedAwake() first (if not already called)
- Then call BroadcastBootComplete()
- Add
Start()method:- Call BroadcastManagedAwake() if boot not complete yet (fallback)
Key Details:
- LifecycleManager should exist in BootstrapScene (persistent)
- Must handle both editor and runtime initialization
- Consider RuntimeInitializeOnLoadMethod for creation if needed
STEP 11: Integrate LifecycleManager with SceneManagerService
File: Assets/Scripts/Core/Lifecycle/LifecycleManager.cs (continued)
Actions:
- In post-boot initialization:
- Subscribe to SceneManagerService.Instance.SceneLoadCompleted
- Subscribe to SceneManagerService.Instance.SceneUnloadStarted
- Implement handler for
SceneLoadCompleted:- Set _isSceneReady = true
- Call BroadcastSceneReady()
- Handle scene-specific vs persistent components (check IsPersistent flag)
- Implement handler for
SceneUnloadStarted:- Set _isSceneReady = false
- Call BroadcastSceneUnloading()
- Only call on scene-specific components (IsPersistent = false)
- In
OnDestroy():- Unsubscribe from all events
Key Details:
- Track which scene each component belongs to (use component.gameObject.scene)
- Only broadcast SceneReady/Unloading to components in affected scene
- Persistent components (BootstrapScene) should not receive scene lifecycle events
STEP 12: Integrate LifecycleManager with GameManager (Pause System)
File: Assets/Scripts/Core/Lifecycle/LifecycleManager.cs (continued)
Actions:
- In post-boot initialization:
- Subscribe to GameManager.Instance.OnGamePaused
- Subscribe to GameManager.Instance.OnGameResumed
- Implement handler for
OnGamePaused:- Set _isPaused = true
- Call BroadcastPause()
- Implement handler for
OnGameResumed:- Set _isPaused = false
- Call BroadcastResume()
Key Details:
- Components can opt-in to pause events via ActiveLifecyclePhases flags
- BroadcastPause/Resume iterates _pauseList only
- Late-registered components get immediate pause state if game is paused
STEP 13: Add Debug and Profiling Tools
File: Assets/Scripts/Core/Lifecycle/LifecycleManager.cs (continued)
Actions:
- Add
[SerializeField] private bool enableDebugLogging = false - Add
[SerializeField] private bool enableProfilingMarkers = true - Create LogDebugMessage() helper that checks flag
- Add Unity Profiler markers around each broadcast phase
- Implement GetRegisteredComponents() method for inspector/debugging
- Add OnGUI() debug overlay showing:
- Registered component counts per phase
- Current lifecycle state flags
- Last broadcast timings
Key Details:
- Use
UnityEngine.Profiling.ProfilerMarkerfor zero-cost profiling in builds - Debug overlay only active in editor or development builds
- Consider creating custom inspector for LifecycleManager
STEP 14: Create LifecycleManager Prefab
File: Assets/Prefabs/Core/LifecycleManager.prefab
Actions:
- Create empty GameObject named "LifecycleManager"
- Add LifecycleManager component
- Configure default settings (debug logging off, profiling on)
- Save as prefab
- Add to BootstrapScene if not already present
- Verify it persists across scenes (should be in BootstrapScene which is additive)
Key Details:
- Only one instance should exist (singleton)
- Should be in BootstrapScene, not in gameplay scenes
- Consider adding validation script to prevent duplicates
STEP 15: Update Bootstrap Integration
File: Assets/Scripts/Bootstrap/BootCompletionService.cs
Actions:
- In
HandleBootCompleted()method, add:- Before executing initialization actions, notify LifecycleManager
- Call
LifecycleManager.Instance?.OnBootCompletionStarting()(new method)
- After initialization actions complete:
- Call existing
OnBootComplete?.Invoke() - LifecycleManager handles this via event subscription
- Call existing
File: Assets/Scripts/Bootstrap/CustomBoot.cs
Actions:
- Verify LifecycleManager exists before CustomBoot completes
- Add fallback: if LifecycleManager doesn't exist, log warning
Key Details:
- BootCompletionService remains authoritative for boot completion
- LifecycleManager observes and broadcasts to ManagedBehaviours
- Maintain backward compatibility with existing RegisterInitAction pattern
STEP 16: Extend SaveLoadManager Integration
File: Assets/Scripts/Core/SaveLoad/SaveLoadManager.cs
Actions:
- Add public method
AutoRegisterParticipant(ISaveParticipant participant, ManagedBehaviour owner):- Same as RegisterParticipant but stores reference to owner ManagedBehaviour
- Allows automatic unregistration when owner is destroyed
- Add public method
AutoUnregisterParticipant(ManagedBehaviour owner):- Looks up participant by owner reference and unregisters
- Store Dictionary<ManagedBehaviour, string> to track auto-registrations
File: Assets/Scripts/Core/Lifecycle/ManagedBehaviour.cs
Actions:
- In Awake() registration:
- Check if
this is ISaveParticipantandAutoRegisterSaveParticipant == true - If yes, call SaveLoadManager.Instance.AutoRegisterParticipant(this as ISaveParticipant, this)
- Check if
- In OnDestroy() cleanup:
- Call SaveLoadManager.Instance.AutoUnregisterParticipant(this)
Key Details:
- Only auto-register after boot completes (SaveLoadManager must be initialized)
- Handle null checks (SaveLoadManager may not exist in editor)
- Log when auto-registration occurs (debug mode)
STEP 17: Extend GameManager Pausable Integration
File: Assets/Scripts/Core/GameManager.cs
Actions:
- Add public method
AutoRegisterPausable(IPausable component, ManagedBehaviour owner):- Same as RegisterPausableComponent but stores owner reference
- Add public method
AutoUnregisterPausable(ManagedBehaviour owner):- Looks up pausable by owner and unregisters
- Store Dictionary<ManagedBehaviour, IPausable> to track auto-registrations
File: Assets/Scripts/Core/Lifecycle/ManagedBehaviour.cs
Actions:
- In post-boot registration (called by LifecycleManager):
- Check if
this is IPausableandAutoRegisterPausable == true - If yes, call GameManager.Instance.AutoRegisterPausable(this as IPausable, this)
- Check if
- In OnDestroy() cleanup:
- Call GameManager.Instance.AutoUnregisterPausable(this)
Key Details:
- Wait until boot completes before registering with GameManager
- Handle case where component implements both ISaveParticipant and IPausable
- Verify GameManager exists before calling
STEP 18: Create Migration Helper Attribute
File: Assets/Scripts/Core/Lifecycle/RequiresManagedBehaviourAttribute.cs
Actions:
- Create custom attribute
[RequiresManagedBehaviour] - Can be applied to classes that should be converted to ManagedBehaviour
- Create custom inspector that shows warning if class derives from MonoBehaviour instead
File: Assets/Editor/ManagedBehaviourValidation.cs
Actions:
- Create editor script that scans project for components with attribute
- Shows list of components needing migration
- Provides "Convert to ManagedBehaviour" button for each
- Automatic string replacement in source files
Key Details:
- Editor-only functionality (Assets/Editor folder)
- Use AssetDatabase to find and modify scripts
- Show before/after preview before conversion
STEP 19: Create Example ManagedBehaviour Implementations
File: Assets/Scripts/Core/Lifecycle/Examples/ExampleManagedSingleton.cs
Actions:
- Create example showing singleton pattern with ManagedBehaviour
- Demonstrate priority usage
- Show lifecycle hook overrides
- Include code comments explaining each part
File: Assets/Scripts/Core/Lifecycle/Examples/ExampleManagedComponent.cs
Actions:
- Create example showing scene-specific component
- Demonstrate managed event subscription
- Show auto-save participant integration
- Show auto-pausable integration
File: Assets/Scripts/Core/Lifecycle/Examples/ExampleManagedManager.cs
Actions:
- Create example showing persistent manager
- Demonstrate all lifecycle phases
- Show priority ordering
- Include performance considerations
Key Details:
- Well-commented code for reference
- Cover common use cases (singleton, scene component, manager)
- Include unit tests if applicable
STEP 20: Migrate Core Systems (Phase 1)
Priority Order: GameManager → SceneManagerService → SaveLoadManager → InputManager
File: Assets/Scripts/Core/GameManager.cs
Actions:
- Change class declaration:
public class GameManager : ManagedBehaviour - Override properties:
protected override int ManagedAwakePriority => 10;(very early)protected override int BootCompletePriority => 10;
- Remove
BootCompletionService.RegisterInitAction(InitializePostBoot)from Awake - Rename
InitializePostBoot()toprotected override void OnBootComplete() - Move Awake logic to
protected override void OnManagedAwake() - Add
protected override LifecycleFlags ActiveLifecyclePhases => LifecycleFlags.ManagedAwake | LifecycleFlags.BootComplete; - Keep existing pause/resume system (don't double-integrate)
- Remove manual singleton setup (handled by base class)
- Test thoroughly
Key Details:
- GameManager initializes early (priority 10)
- Keep existing functionality intact
- Verify settings loading still works
- Ensure pause system integration works
STEP 21: Migrate Core Systems (Phase 2)
Target: SceneManagerService, SaveLoadManager, InputManager, ItemManager
For Each File:
- Change base class to ManagedBehaviour
- Set appropriate priorities (15-30 range)
- Move InitializePostBoot → OnBootComplete
- Remove manual BootCompletionService registration
- Add ActiveLifecyclePhases flags
- Test scene transitions, save/load, input handling
Priorities:
- SceneManagerService: 15 (after GameManager)
- SaveLoadManager: 20 (after SceneManagerService)
- InputManager: 25 (after SaveLoadManager)
- ItemManager: 30 (general manager)
Key Details:
- Verify event subscriptions still work
- Check scene loading/unloading
- Validate save/load system
- Test input handling in different modes
STEP 22: Migrate UI Systems
Target: UIPageController, LoadingScreenController, PauseMenu, CardSystemUI components
For Each File:
- Change base class to ManagedBehaviour
- Set priority 50-100 (standard range)
- Convert InitializePostBoot → OnBootComplete
- Use RegisterManagedEvent for event subscriptions
- Add OnSceneReady override if needed for scene-specific UI
- Test UI navigation, loading screens, pause menu
Key Details:
- UI generally has lower priority than managers
- Scene-specific UI should override OnSceneReady
- Use AutoRegisterSaveParticipant for UI with state
STEP 23: Migrate Gameplay Systems
Target: PuzzleManager, DivingGameManager, CinematicsManager, CardSystemManager
For Each File:
- Change base class to ManagedBehaviour
- Set priority 100-200 (gameplay systems)
- Convert InitializePostBoot → OnBootComplete
- Use OnSceneReady for scene-specific setup
- Use OnSceneUnloading for cleanup
- Consider AutoRegisterSaveParticipant
- Test gameplay features
Key Details:
- Scene-specific managers should handle OnSceneReady/Unloading
- Persistent managers (like CardSystemManager) set IsPersistent = true
- Verify minigame transitions work correctly
STEP 24: Migrate Interaction Systems
Target: Interactable, InteractionActionBase, DialogueComponent
File: Assets/Scripts/Interactions/InteractionActionBase.cs
Actions:
- Change base class from MonoBehaviour to ManagedBehaviour
- Change
protected virtual void Awake()toprotected override void OnManagedAwake() - Keep OnEnable/OnDisable as-is (Unity lifecycle needed for enable/disable)
- Consider using RegisterManagedEvent for parentInteractable events
Key Details:
- InteractionActionBase is itself a base class (virtual methods remain)
- Subclasses inherit ManagedBehaviour capabilities
- Test interaction flow thoroughly
STEP 25: Migrate Player and Movement Systems
Target: PlayerTouchController, FollowerController
For Each File:
- Change base class to ManagedBehaviour
- Keep Update() as Unity Update (movement needs every frame)
- Use OnBootComplete for post-boot initialization
- Use RegisterManagedEvent for InputManager event subscriptions
- Test movement, pathfinding, interactions
Key Details:
- Performance-critical components should use Unity Update, not OnManagedUpdate
- Only use ManagedBehaviour for initialization/cleanup benefits
- Verify A* pathfinding still works
STEP 26: Update InteractionActionBase Pattern
File: Assets/Scripts/Interactions/InteractionActionBase.cs
Actions:
- Since this is a base class, ensure subclasses can override lifecycle hooks
- Add example subclass showing proper override chain
- Document pattern for action components
Pattern:
public class MyAction : InteractionActionBase
{
protected override void OnManagedAwake()
{
base.OnManagedAwake(); // Calls parent registration
// Custom initialization
}
}
Key Details:
- Base.OnManagedAwake() must be called in subclasses
- Provide clear documentation for this pattern
- Consider sealed methods if override not intended
STEP 27: Create Validation and Testing Suite
File: Assets/Editor/Tests/LifecycleSystemTests.cs
Actions:
- Create Unity Test Runner tests for:
- Registration/unregistration
- Priority ordering
- Lifecycle hook execution order
- Event subscription cleanup
- Save/load integration
- Pause system integration
- Scene transition handling
- Create test ManagedBehaviour subclasses
- Mock managers for isolated testing
File: Assets/Scripts/Core/Lifecycle/LifecycleValidator.cs
Actions:
- Create runtime validator that checks:
- No duplicate registrations
- All ManagedBehaviours properly registered
- Priority conflicts (warnings)
- Missing overrides (if flags set but no override)
- Add menu item "Tools/Lifecycle/Validate Scene"
Key Details:
- Tests should cover all lifecycle phases
- Include edge cases (late registration, scene transitions)
- Performance benchmarks for Update broadcasts
STEP 28: Create Documentation
File: docs/ManagedBehaviour_Usage_Guide.md
Content:
- Quick start guide
- Common patterns and examples
- Lifecycle phase reference
- Priority guidelines
- Performance considerations
- Migration guide from MonoBehaviour
- Troubleshooting section
File: docs/ManagedBehaviour_API_Reference.md
Content:
- Complete API documentation
- All virtual methods with parameters
- All properties and their defaults
- Integration points (SaveLoadManager, GameManager)
- Best practices
File: docs/ManagedBehaviour_Architecture.md
Content:
- System architecture diagram
- Component interaction flows
- Performance characteristics
- Design decisions and rationale
- Future extension points
STEP 29: Performance Optimization Pass
File: Assets/Scripts/Core/Lifecycle/LifecycleManager.cs
Actions:
- Profile Update/FixedUpdate broadcasts
- Consider using Unity Jobs for parallel execution (if applicable)
- Add object pooling for event subscriptions
- Cache frequently accessed lists
- Add compile-time flags for debug features
- Optimize sort algorithms for registration
- Consider lazy initialization for empty lists
Targets:
- Registration: < 0.1ms per component
- Update broadcast: < 0.5ms for 100 components
- Memory overhead: < 100 bytes per ManagedBehaviour
Key Details:
- Use Unity Profiler to identify bottlenecks
- Compare against baseline (vanilla MonoBehaviour)
- Optimize hot paths (Update broadcasts)
STEP 30: Create Editor Tools
File: Assets/Editor/LifecycleManagerInspector.cs
Actions:
- Custom inspector for LifecycleManager
- Show registered components grouped by phase
- Display component priorities
- Show current state flags
- Add "Force Broadcast" buttons for testing
- Real-time component count display
File: Assets/Editor/ManagedBehaviourInspector.cs
Actions:
- Custom inspector for ManagedBehaviour
- Show active lifecycle phases (visual flags)
- Display current registration status
- Show priority values
- List registered managed events
- Add "Test Lifecycle" button
File: Assets/Editor/LifecycleDebugWindow.cs
Actions:
- Custom editor window "Window/Lifecycle/Debug"
- Real-time lifecycle event log
- Component hierarchy view
- Priority visualization
- Performance metrics
- Event subscription viewer
STEP 31: Final Integration Testing
Test Scenarios:
-
Boot Sequence Test:
- Start game from BootstrapScene
- Verify all lifecycle phases execute in order
- Check priority ordering is respected
- Validate settings load correctly
-
Scene Transition Test:
- Switch between multiple scenes
- Verify OnSceneReady called for new scenes
- Verify OnSceneUnloading called for old scenes
- Check persistent components don't receive scene events
-
Save/Load Test:
- Save game state
- Switch scenes
- Load saved state
- Verify all ISaveParticipant components restored
-
Pause System Test:
- Pause game (multiple requests)
- Verify all pausable components paused
- Resume game
- Verify all components resumed
-
Late Registration Test:
- Instantiate ManagedBehaviour after boot
- Verify it receives appropriate lifecycle calls
- Check it gets current state (paused, scene ready, etc.)
-
Destruction Test:
- Destroy ManagedBehaviours during gameplay
- Verify cleanup happens correctly
- Check no memory leaks (event subscriptions cleaned)
-
Performance Test:
- Spawn 100+ ManagedBehaviours
- Measure frame time impact
- Profile Update broadcasts
- Check memory usage
STEP 32: Migration Strategy for Remaining Components
Approach: Gradual, category-by-category migration
Phase 1: Critical Path (Already covered in steps 20-25)
- Core managers
- UI systems
- Gameplay systems
Phase 2: Scene-Specific Components
- Level switches
- Minigame components
- Puzzle elements
- Scene triggers
Phase 3: Utility Components
- Camera systems
- Audio managers
- Effect spawners
- Pooling systems
Phase 4: Low-Priority/External
- Experimental scripts
- Test components
- External plugin wrappers
For Each Component:
- Assess if it benefits from ManagedBehaviour (needs lifecycle control?)
- If yes: follow migration pattern (change base class, update hooks)
- If no: leave as MonoBehaviour (simple utility scripts)
- Test after each file migrated
- Commit to version control
Key Details:
- Not all components need migration (only those needing lifecycle control)
- Prioritize components with InitializePostBoot, scene event subscriptions, or complex initialization
- Simple components (visual effects, UI elements) can stay MonoBehaviour
- Document migration decisions in code comments
STEP 33: Backward Compatibility Layer
File: Assets/Scripts/Core/Lifecycle/LegacyMonoBehaviourAdapter.cs
Actions:
- Create adapter class for existing MonoBehaviour components
- Allows non-migrated components to participate in lifecycle
- Implement manual registration helper:
public static class LifecycleHelper { public static void RegisterLegacyComponent(MonoBehaviour component, Action onBootComplete) { BootCompletionService.RegisterInitAction(onBootComplete); } } - Document when to use adapter vs. migration
Key Details:
- Provides bridge during migration period
- Allows incremental adoption
- Eventually can be deprecated once migration complete
STEP 34: Code Cleanup and Refactoring
Actions:
-
Remove Redundant Code:
- Search for
BootCompletionService.RegisterInitActionin migrated files (should be removed) - Search for manual event subscription patterns (Convert to RegisterManagedEvent)
- Remove empty InitializePostBoot methods
- Search for
-
Standardize Patterns:
- Consistent priority values across similar components
- Consistent ActiveLifecyclePhases usage
- Consistent logging patterns
-
Update Comments:
- Remove outdated references to old lifecycle
- Add references to ManagedBehaviour lifecycle
- Document priority decisions
-
Code Review:
- Review all migrated files
- Check for proper base.OnManagedAwake() calls
- Verify no breaking changes to public APIs
STEP 35: Update Project Documentation
Files to Update:
-
docs/bootstrap_readme.md:- Add section on ManagedBehaviour integration
- Update lifecycle flow diagrams
- Reference new lifecycle guide
-
README.md:- Add link to ManagedBehaviour documentation
- Update project structure description
-
Create
docs/ManagedBehaviour_Migration_Log.md:- List of migrated components
- Migration dates
- Issues encountered and solutions
- Components intentionally not migrated
-
Update inline documentation:
- Update XML comments in migrated files
- Reference ManagedBehaviour lifecycle in comments
- Add examples where helpful
STEP 36: Performance Profiling and Optimization
Actions:
-
Baseline Measurements:
- Measure current performance (before optimization)
- Profile Update broadcasts (100+ components)
- Memory allocation tracking
- GC pressure measurement
-
Optimization Targets:
- LifecycleManager.Update < 0.5ms (100 components)
- LifecycleManager.FixedUpdate < 0.3ms (100 components)
- Registration < 0.1ms per component
- Zero GC allocations during broadcasts
-
Implementation:
- Cache component counts
- Use for loops instead of foreach (no allocator)
- Avoid boxing in generic methods
- Pool event subscription objects
- Use struct enumerators
-
Validation:
- Re-profile after optimizations
- Compare against baseline
- Ensure optimizations don't break functionality
STEP 37: Edge Case Handling
Scenarios to Handle:
-
Editor Mode:
- Handle domain reload
- Handle enter/exit play mode
- Handle script recompilation during play
-
Build Scenarios:
- Development builds (debug logging enabled)
- Release builds (debug code stripped)
- Platform-specific behavior
-
Error Recovery:
- Handle missing LifecycleManager gracefully
- Handle null component references
- Handle exceptions during lifecycle hooks (isolate failures)
-
Threading:
- Ensure thread safety for registration queue
- Handle registration from background threads (queue for main thread)
-
Scene Edge Cases:
- Handle multiple scenes loaded additively
- Handle rapid scene switches
- Handle scene unload during lifecycle broadcast
Implementation: Add defensive checks, logging, and fallbacks throughout LifecycleManager and ManagedBehaviour.
STEP 38: Create Tutorial Scene
File: Assets/Scenes/ManagedBehaviourTutorial.unity
Actions:
- Create tutorial scene demonstrating system
- Include examples of:
- Simple ManagedBehaviour
- Priority ordering demonstration
- Lifecycle phase visualization
- Event subscription examples
- Save/load integration
- Pause system integration
- Add UI showing lifecycle events in real-time
- Include interactive elements to trigger phases
File: Assets/Scripts/Tutorial/LifecycleEventLogger.cs
Actions:
- Create component that logs all lifecycle events to UI
- Shows order of execution
- Shows timing information
- Color-codes by priority
STEP 39: Community Documentation and Examples
File: docs/ManagedBehaviour_FAQ.md
Content:
- Frequently asked questions
- Common mistakes and solutions
- When to use ManagedBehaviour vs MonoBehaviour
- Performance considerations
- Debugging tips
File: docs/ManagedBehaviour_Recipes.md
Content:
- Common patterns and recipes
- Singleton pattern with ManagedBehaviour
- Scene-specific manager pattern
- Persistent service pattern
- Component with managed events pattern
- Save/load participant pattern
STEP 40: Final Review and Release Preparation
Checklist:
- All core systems migrated
- All tests passing
- Performance targets met
- Documentation complete
- Examples created and tested
- Editor tools working
- No console errors or warnings
- Code reviewed
- Backward compatibility maintained
- Migration guide complete
Final Actions:
- Create release notes
- Tag version in git
- Update project version number
- Create migration checklist for team
- Schedule team training/walkthrough
- Monitor for issues after deployment
Appendix A: Priority Guidelines
Recommended Priority Ranges
| Component Type | Priority Range | Reasoning |
|---|---|---|
| Core Infrastructure | 0-20 | LifecycleManager, CustomBoot integration |
| Core Managers | 10-30 | GameManager, SceneManager, SaveManager |
| Service Providers | 30-50 | Settings providers, factories |
| Gameplay Managers | 50-100 | PuzzleManager, CardSystem, Minigames |
| UI Controllers | 100-150 | Page controllers, loading screens |
| Scene Components | 150-200 | Level-specific logic |
| Gameplay Objects | 200-500 | Interactables, pickups, NPCs |
| Visual Effects | 500-1000 | Particles, animations, decorative |
Priority Best Practices
- Use increments of 5 or 10 for easy insertion
- Document priority decisions in code comments
- Avoid priority 0 (reserved for system use)
- Group related components in same priority range
- Consider dependencies when assigning priorities
Appendix B: Common Migration Patterns
Pattern 1: Basic Singleton Manager
Before:
public class MyManager : MonoBehaviour
{
private static MyManager _instance;
public static MyManager Instance => _instance;
void Awake()
{
_instance = this;
BootCompletionService.RegisterInitAction(InitializePostBoot);
}
private void InitializePostBoot()
{
// Setup
}
}
After:
public class MyManager : ManagedBehaviour
{
private static MyManager _instance;
public static MyManager Instance => _instance;
protected override int BootCompletePriority => 50;
protected override LifecycleFlags ActiveLifecyclePhases =>
LifecycleFlags.ManagedAwake | LifecycleFlags.BootComplete;
protected override void OnManagedAwake()
{
_instance = this;
}
protected override void OnBootComplete()
{
// Setup
}
}
Pattern 2: Component with Event Subscriptions
Before:
public class MyComponent : MonoBehaviour
{
void Start()
{
SomeManager.Instance.OnEvent += HandleEvent;
}
void OnDestroy()
{
if (SomeManager.Instance != null)
SomeManager.Instance.OnEvent -= HandleEvent;
}
private void HandleEvent() { }
}
After:
public class MyComponent : ManagedBehaviour
{
protected override LifecycleFlags ActiveLifecyclePhases =>
LifecycleFlags.BootComplete;
protected override void OnBootComplete()
{
RegisterManagedEvent(SomeManager.Instance,
m => m.OnEvent += HandleEvent);
}
private void HandleEvent() { }
// No OnDestroy needed - auto cleanup
}
Pattern 3: Scene-Specific Component
Before:
public class LevelComponent : MonoBehaviour
{
void Start()
{
SceneManagerService.Instance.SceneLoadCompleted += OnSceneLoaded;
Initialize();
}
void OnDestroy()
{
if (SceneManagerService.Instance != null)
SceneManagerService.Instance.SceneLoadCompleted -= OnSceneLoaded;
}
private void OnSceneLoaded(string sceneName) { }
private void Initialize() { }
}
After:
public class LevelComponent : ManagedBehaviour
{
protected override LifecycleFlags ActiveLifecyclePhases =>
LifecycleFlags.SceneReady | LifecycleFlags.SceneUnloading;
protected override void OnSceneReady()
{
Initialize();
}
protected override void OnSceneUnloading()
{
Cleanup();
}
private void Initialize() { }
private void Cleanup() { }
}
Appendix C: Troubleshooting Guide
Issue: Lifecycle hook not being called
Symptoms: OnBootComplete or other hook not executing Solutions:
- Check ActiveLifecyclePhases includes the appropriate flag
- Verify LifecycleManager exists in scene
- Ensure base.OnManagedAwake() called if overriding
- Check LifecycleManager debug logs
Issue: Wrong execution order
Symptoms: Components initialize in unexpected order Solutions:
- Verify priority values (lower = earlier)
- Check for priority conflicts (same priority = undefined order)
- Add debug logging to verify execution order
- Use LifecycleDebugWindow to visualize order
Issue: Events not cleaning up
Symptoms: Memory leaks or errors after component destruction Solutions:
- Use RegisterManagedEvent instead of manual subscription
- Verify OnDestroy calls base.OnDestroy()
- Check event target is not null when registering
- Review LifecycleManager event cleanup logs
Issue: Performance degradation
Symptoms: Frame time increases with many ManagedBehaviours Solutions:
- Only override lifecycle hooks you need
- Don't use OnManagedUpdate if Unity Update suffices
- Profile with Unity Profiler to find bottleneck
- Check for excessive logging in release builds
- Optimize ActiveLifecyclePhases (don't include unused phases)
Appendix D: Integration Checklist
Use this checklist when migrating each component:
Pre-Migration:
- Component has InitializePostBoot or complex initialization
- Component subscribes to manager events
- Component needs deterministic initialization order
- Component is ISaveParticipant or IPausable
Migration:
- Changed base class to ManagedBehaviour
- Set appropriate priority values
- Added ActiveLifecyclePhases flags
- Moved Awake logic to OnManagedAwake
- Moved InitializePostBoot to OnBootComplete
- Converted event subscriptions to RegisterManagedEvent
- Removed manual BootCompletionService registration
- Set AutoRegisterSaveParticipant if ISaveParticipant
- Set AutoRegisterPausable if IPausable
- Updated XML documentation
Post-Migration:
- No compiler errors
- Component still functions correctly
- Lifecycle hooks called at appropriate times
- Priority ordering correct relative to dependencies
- No memory leaks (events cleaned up)
- Performance acceptable
- Code reviewed
- Tests updated/added
Appendix E: Future Enhancements
Potential future additions to the system:
- Async Lifecycle Hooks: Support for async/await in lifecycle methods
- Conditional Phases: Enable/disable phases at runtime
- Lifecycle Groups: Group related components for batch operations
- Visual Editor: Node-based lifecycle flow visualization
- Hot Reload Support: Better handling of domain reload in editor
- Multi-threading: Parallel execution of independent components
- Lifecycle Templates: Pre-configured setups for common patterns
- Analytics Integration: Track lifecycle performance in production
- Memory Pooling: Pool ManagedBehaviour instances for performance
- Dependency Injection: Auto-resolve dependencies in OnManagedAwake
Document Metadata
Version: 1.0
Last Updated: October 30, 2025
Author: AI Assistant
Status: Ready for Implementation
Implementation Estimate: 40-60 hours across 2-3 weeks
Risk Level: Medium (significant refactoring, but incremental approach reduces risk)
Prerequisites: Unity 2021.3+, C# 8.0+, existing bootstrap system
Next Steps:
- Review this document thoroughly
- Get team approval
- Begin with STEP 1
- Progress sequentially through steps
- Test thoroughly at each phase
- Document any deviations or issues