Compare commits
1 Commits
7307971748
...
ef1dcc7699
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef1dcc7699 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -104,3 +104,6 @@ InitTestScene*.unity*
|
|||||||
.vscode/launch.json
|
.vscode/launch.json
|
||||||
.vscode/settings.json
|
.vscode/settings.json
|
||||||
.idea/.idea.AppleHillsProduction/.idea/indexLayout.xml
|
.idea/.idea.AppleHillsProduction/.idea/indexLayout.xml
|
||||||
|
|
||||||
|
# WIP docs
|
||||||
|
/docs/wip/
|
||||||
|
|||||||
93
docs/managed_behavior/architecture_overview.md
Normal file
93
docs/managed_behavior/architecture_overview.md
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
# ManagedBehaviour System - Architecture Overview
|
||||||
|
|
||||||
|
**Version:** 2.0 <br>
|
||||||
|
**Updated:** 11.11.2025
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What is the ManagedBehaviour System?
|
||||||
|
|
||||||
|
Lifecycle orchestration framework that provides guaranteed execution order and deterministic lifecycle management for Unity components.
|
||||||
|
|
||||||
|
### Problems Solved
|
||||||
|
|
||||||
|
We've had quite a few things shoe-stringed together in various ways and dependant on references to each other.
|
||||||
|
Due to undefined initialization order - null references during access to yet uninitialized resources was becoming a problem.
|
||||||
|
|
||||||
|
### What You Get
|
||||||
|
|
||||||
|
- **Guaranteed Initialization Order** - Managers ready before components that depend on them
|
||||||
|
- **Deterministic Lifecycle Hooks** - Predictable callbacks at key moments
|
||||||
|
- **Automatic Registration** - No boilerplate for wiring up systems
|
||||||
|
- **Scene Lifecycle Events** - Built-in hooks for scene load/unload
|
||||||
|
- **Save/Load Coordination** - Centralized collection of save data
|
||||||
|
- **Bootstrap Integration** - Components know when bootstrap completes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture Principles
|
||||||
|
|
||||||
|
### 1. Centralized Orchestration
|
||||||
|
|
||||||
|
Single `LifecycleManager` singleton coordinates all lifecycle events. Components auto-register and receive callbacks at appropriate times.
|
||||||
|
|
||||||
|
### 2. Sealed Framework Methods
|
||||||
|
|
||||||
|
`Awake()` and `OnDestroy()` are sealed. Use `OnManagedAwake()` and `OnManagedDestroy()` instead. Prevents forgetting to call `base.Awake()`.
|
||||||
|
|
||||||
|
### 3. Two-Phase Initialization
|
||||||
|
|
||||||
|
- **Early (`OnManagedAwake()`)**: During Unity's Awake, before bootstrap. Use for singleton setup.
|
||||||
|
- **Late (`OnManagedStart()`)**: After bootstrap completes. All managers guaranteed ready.
|
||||||
|
|
||||||
|
### 4. Registration Order Execution
|
||||||
|
|
||||||
|
Execution follows Unity's natural Awake order. No priority numbers to manage.
|
||||||
|
|
||||||
|
### 5. Automatic Cleanup
|
||||||
|
|
||||||
|
Framework handles unregistration automatically. Override `OnManagedDestroy()` only if you need custom cleanup.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Lifecycle Flow Diagrams
|
||||||
|
|
||||||
|
### Boot Sequence
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Scene Transition Flow
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Component Lifecycle (Individual Component)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Class Diagram
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
## Key Guarantees
|
||||||
|
|
||||||
|
### Guaranteed
|
||||||
|
|
||||||
|
1. **Bootstrap Completion** - `OnManagedStart()` always fires after bootstrap completes
|
||||||
|
2. **Manager Availability** - All manager singletons exist when `OnManagedStart()` is called
|
||||||
|
3. **Scene Lifecycle** - `OnSceneReady()` fires after scene load, `OnSceneUnloading()` before unload
|
||||||
|
4. **Automatic Registration** - Can't forget to register (Awake is sealed)
|
||||||
|
5. **Automatic Cleanup** - Can't forget to unregister (OnDestroy is sealed)
|
||||||
|
6. **Save/Load Coordination** - All save participants called in one pass
|
||||||
|
|
||||||
|
### Not Guaranteed
|
||||||
|
|
||||||
|
1. **Initialization Order Between Components** - `OnManagedAwake()` follows Unity's unpredictable Awake order
|
||||||
|
2. **Thread Safety** - All methods must run on main thread
|
||||||
|
3. **Performance** - Broadcasting to 1000+ components may have overhead
|
||||||
|
4. **SaveId Uniqueness** - Developer responsible for unique SaveIds
|
||||||
|
|
||||||
|
|
||||||
377
docs/managed_behavior/technical_reference.md
Normal file
377
docs/managed_behavior/technical_reference.md
Normal file
@@ -0,0 +1,377 @@
|
|||||||
|
# ManagedBehaviour System - Technical Reference
|
||||||
|
|
||||||
|
**Version:** 2.0 <br>
|
||||||
|
**Updated:** 11.11.2025
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The ManagedBehaviour system provides a deterministic, ordered lifecycle management framework for Unity MonoBehaviours. This document provides complete technical documentation of all classes, methods, and APIs.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Core Classes
|
||||||
|
|
||||||
|
### ManagedBehaviour (Abstract Base Class)
|
||||||
|
|
||||||
|
**Namespace:** `Core.Lifecycle`
|
||||||
|
**Inherits:** `MonoBehaviour`
|
||||||
|
**Location:** `Assets/Scripts/Core/Lifecycle/ManagedBehaviour.cs` → [View Source](../../Assets/Scripts/Core/Lifecycle/ManagedBehaviour.cs)
|
||||||
|
|
||||||
|
Abstract base class that all managed components must inherit from. Provides automatic registration with LifecycleManager and ordered lifecycle callbacks.
|
||||||
|
|
||||||
|
#### Lifecycle Hook Methods
|
||||||
|
|
||||||
|
Override these `internal virtual` methods to customize component behavior at different lifecycle stages. Called automatically by `LifecycleManager`.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Click to see more details</summary>
|
||||||
|
|
||||||
|
##### `OnManagedAwake()`
|
||||||
|
```csharp
|
||||||
|
internal virtual void OnManagedAwake()
|
||||||
|
```
|
||||||
|
**Called:** During registration (within Unity's Awake phase)
|
||||||
|
**Execution Order:** Natural Unity script execution order (not guaranteed between components)
|
||||||
|
**Use For:**
|
||||||
|
- Setting singleton instances (`_instance = this`)
|
||||||
|
- Early GetComponent calls
|
||||||
|
- One-time initialization that doesn't depend on other systems
|
||||||
|
|
||||||
|
**Timing Guarantees:**
|
||||||
|
- ✅ GameObject and component exist
|
||||||
|
- ✅ Scene is loaded
|
||||||
|
- ❌ Other components may not be initialized yet
|
||||||
|
- ❌ Bootstrap may not be complete
|
||||||
|
|
||||||
|
##### `OnManagedStart()`
|
||||||
|
```csharp
|
||||||
|
internal virtual void OnManagedStart()
|
||||||
|
```
|
||||||
|
**Called:** After bootstrap completes (for boot components) or immediately after registration (for late-registered components)
|
||||||
|
**Execution Order:** Registration order
|
||||||
|
**Use For:**
|
||||||
|
- Initialization that depends on other managers
|
||||||
|
- Accessing singleton instances safely
|
||||||
|
- Setting up cross-system dependencies
|
||||||
|
|
||||||
|
**Timing Guarantees:**
|
||||||
|
- ✅ All managers are initialized
|
||||||
|
- ✅ Bootstrap resources are available
|
||||||
|
- ✅ Safe to access `GameManager.Instance`, `AudioManager.Instance`, etc.
|
||||||
|
|
||||||
|
##### `OnSceneUnloading()`
|
||||||
|
```csharp
|
||||||
|
internal virtual void OnSceneUnloading()
|
||||||
|
```
|
||||||
|
**Called:** Before scene unload
|
||||||
|
**Execution Order:** Registration order
|
||||||
|
**Use For:**
|
||||||
|
- Scene-specific cleanup
|
||||||
|
- Saving temporary scene state
|
||||||
|
|
||||||
|
**Timing Guarantees:**
|
||||||
|
- ✅ Scene is still loaded
|
||||||
|
- ✅ Other components in scene still exist
|
||||||
|
|
||||||
|
##### `OnSceneReady()`
|
||||||
|
```csharp
|
||||||
|
internal virtual void OnSceneReady()
|
||||||
|
```
|
||||||
|
**Called:** After scene load completes
|
||||||
|
**Execution Order:** Registration order
|
||||||
|
**Use For:**
|
||||||
|
- Scene-specific initialization
|
||||||
|
- Finding scene objects
|
||||||
|
- Setting up scene-specific state
|
||||||
|
|
||||||
|
**Timing Guarantees:**
|
||||||
|
- ✅ All scene GameObjects are loaded
|
||||||
|
- ✅ Batched components have received `OnManagedStart()`
|
||||||
|
|
||||||
|
##### `OnSceneSaveRequested()`
|
||||||
|
```csharp
|
||||||
|
internal virtual string OnSceneSaveRequested()
|
||||||
|
```
|
||||||
|
**Called:** Before scene unload during scene transitions
|
||||||
|
**Returns:** Serialized scene-specific data (JSON string), or `null` if nothing to save
|
||||||
|
**Use For:**
|
||||||
|
- Level progress
|
||||||
|
- Object positions
|
||||||
|
- Puzzle states
|
||||||
|
- Temporary scene data
|
||||||
|
|
||||||
|
**⚠️ Important:** Must return synchronously.
|
||||||
|
|
||||||
|
##### `OnSceneRestoreRequested(string serializedData)`
|
||||||
|
```csharp
|
||||||
|
internal virtual void OnSceneRestoreRequested(string serializedData)
|
||||||
|
```
|
||||||
|
**Called:** After scene load, during `OnSceneReady` phase
|
||||||
|
**Parameters:**
|
||||||
|
- `serializedData` - Previously saved data from `OnSceneSaveRequested()`
|
||||||
|
|
||||||
|
**Use For:**
|
||||||
|
- Restoring level progress
|
||||||
|
- Setting object positions
|
||||||
|
- Restoring puzzle states
|
||||||
|
|
||||||
|
**⚠️ Important:** Must execute synchronously. Do not use coroutines or async/await.
|
||||||
|
|
||||||
|
##### `OnSceneRestoreCompleted()`
|
||||||
|
```csharp
|
||||||
|
internal virtual void OnSceneRestoreCompleted()
|
||||||
|
```
|
||||||
|
**Called:** After all `OnSceneRestoreRequested()` calls complete
|
||||||
|
**Timing:** Always called after scene load, whether save data exists or not
|
||||||
|
**Use For:**
|
||||||
|
- Post-restore initialization
|
||||||
|
- First-time initialization (when no save data exists)
|
||||||
|
- Triggering events after state is restored
|
||||||
|
|
||||||
|
**Common Pattern:**
|
||||||
|
```csharp
|
||||||
|
internal override void OnSceneRestoreCompleted()
|
||||||
|
{
|
||||||
|
if (!_hasPlayed) // Check if this is first time
|
||||||
|
{
|
||||||
|
PlayIntroAudio();
|
||||||
|
_hasPlayed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### `OnGlobalSaveRequested()`
|
||||||
|
```csharp
|
||||||
|
internal virtual string OnGlobalSaveRequested()
|
||||||
|
```
|
||||||
|
**Called:** Once before save file is written to disk
|
||||||
|
**Returns:** Serialized global persistent data (JSON string), or `null`
|
||||||
|
**Use For:**
|
||||||
|
- Player inventory
|
||||||
|
- Unlocked features
|
||||||
|
- Card collections
|
||||||
|
- Persistent progression
|
||||||
|
|
||||||
|
**Timing:** Called once per game save (not per scene transition)
|
||||||
|
|
||||||
|
##### `OnGlobalRestoreRequested(string serializedData)`
|
||||||
|
```csharp
|
||||||
|
internal virtual void OnGlobalRestoreRequested(string serializedData)
|
||||||
|
```
|
||||||
|
**Called:** Once on game boot after save file is read
|
||||||
|
**Parameters:**
|
||||||
|
- `serializedData` - Previously saved data from `OnGlobalSaveRequested()`
|
||||||
|
|
||||||
|
**Use For:**
|
||||||
|
- Restoring player inventory
|
||||||
|
- Restoring unlocked features
|
||||||
|
- Restoring persistent progression
|
||||||
|
|
||||||
|
**Timing:** Called once on boot, not during scene transitions
|
||||||
|
|
||||||
|
##### `OnGlobalLoadCompleted()`
|
||||||
|
```csharp
|
||||||
|
internal virtual void OnGlobalLoadCompleted()
|
||||||
|
```
|
||||||
|
**Called:** Once on game boot after all global restore operations complete
|
||||||
|
**Use For:**
|
||||||
|
- Triggering UI updates after load
|
||||||
|
- Broadcasting load events
|
||||||
|
- Post-load initialization
|
||||||
|
|
||||||
|
##### `OnGlobalSaveStarted()`
|
||||||
|
```csharp
|
||||||
|
internal virtual void OnGlobalSaveStarted()
|
||||||
|
```
|
||||||
|
**Called:** Once before save file is written
|
||||||
|
**Use For:**
|
||||||
|
- Final validation before save
|
||||||
|
- Cleanup operations before save
|
||||||
|
|
||||||
|
##### `OnManagedDestroy()`
|
||||||
|
```csharp
|
||||||
|
internal virtual void OnManagedDestroy()
|
||||||
|
```
|
||||||
|
**Called:** During `OnDestroy`, before unregistration
|
||||||
|
**Execution Order:** Registration order
|
||||||
|
**Use For:**
|
||||||
|
- Unsubscribing from events
|
||||||
|
- Releasing resources
|
||||||
|
- Custom cleanup logic
|
||||||
|
|
||||||
|
**Note:** Most cleanup is automatic (auto-registrations are handled by framework).
|
||||||
|
</details>
|
||||||
|
|
||||||
|
#### Configuration Properties
|
||||||
|
|
||||||
|
Virtual properties that control automatic behaviors like pause registration and save system participation.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Click to see more details</summary>
|
||||||
|
|
||||||
|
##### `AutoRegisterPausable`
|
||||||
|
```csharp
|
||||||
|
public virtual bool AutoRegisterPausable => false;
|
||||||
|
```
|
||||||
|
**Type:** `bool`
|
||||||
|
**Default:** `false`
|
||||||
|
**Description:** If true and component implements `IPausable`, automatically registers with `GameManager.Instance` during initialization. Automatic unregistration occurs on destruction.
|
||||||
|
|
||||||
|
##### `AutoRegisterForSave`
|
||||||
|
```csharp
|
||||||
|
public virtual bool AutoRegisterForSave => false;
|
||||||
|
```
|
||||||
|
**Type:** `bool`
|
||||||
|
**Default:** `false`
|
||||||
|
**Description:** If true, component participates in the save/load system. Should override `OnSceneSaveRequested()` and `OnSceneRestoreRequested()` or global equivalents.
|
||||||
|
|
||||||
|
##### `SaveId`
|
||||||
|
```csharp
|
||||||
|
public virtual string SaveId { get; }
|
||||||
|
```
|
||||||
|
**Type:** `string`
|
||||||
|
**Default:** `"{SceneName}/{GameObjectName}/{ComponentType}"`
|
||||||
|
**Description:** Unique identifier for this component in the save system. Cached on first access for performance. Override for singletons (e.g., `"PlayerController"`) or custom IDs.
|
||||||
|
|
||||||
|
**⚠️ Warning:** GameObject name changes at runtime will NOT update the cached SaveId.
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### Private Lifecycle Methods
|
||||||
|
<details>
|
||||||
|
<summary>Click to see more details</summary>
|
||||||
|
|
||||||
|
##### `Awake()`
|
||||||
|
```csharp
|
||||||
|
private void Awake()
|
||||||
|
```
|
||||||
|
**Visibility:** `private` (sealed, cannot be overridden)
|
||||||
|
**Called By:** Unity
|
||||||
|
**Description:** Automatically registers component with `LifecycleManager`. Calls `OnManagedAwake()` during registration.
|
||||||
|
|
||||||
|
**⚠️ Important:** This method is sealed. Use `OnManagedAwake()` for early initialization.
|
||||||
|
|
||||||
|
##### `OnDestroy()`
|
||||||
|
```csharp
|
||||||
|
private void OnDestroy()
|
||||||
|
```
|
||||||
|
**Visibility:** `private` (sealed, cannot be overridden)
|
||||||
|
**Called By:** Unity
|
||||||
|
**Description:** Calls `OnManagedDestroy()`, unregisters from `LifecycleManager`, and handles auto-unregistrations.
|
||||||
|
|
||||||
|
**⚠️ Important:** This method is sealed. Use `OnManagedDestroy()` for custom cleanup.
|
||||||
|
</details>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## LifecycleManager (Singleton Orchestrator)
|
||||||
|
|
||||||
|
**Namespace:** `Core.Lifecycle`
|
||||||
|
**Inherits:** `MonoBehaviour`
|
||||||
|
**Location:** `Assets/Scripts/Core/Lifecycle/LifecycleManager.cs` → [View Source](../../Assets/Scripts/Core/Lifecycle/LifecycleManager.cs)
|
||||||
|
|
||||||
|
Central orchestrator that manages all `ManagedBehaviour` components and broadcasts lifecycle events.
|
||||||
|
|
||||||
|
### Static Properties
|
||||||
|
|
||||||
|
##### `Instance`
|
||||||
|
Singleton instance. Created automatically by `CustomBoot` before bootstrap begins.
|
||||||
|
|
||||||
|
### Public Methods
|
||||||
|
|
||||||
|
Core methods for registration, lifecycle broadcasting, and save/restore operations. Most are called automatically by the framework.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Click to see more details</summary>
|
||||||
|
|
||||||
|
##### `Register(ManagedBehaviour component)`
|
||||||
|
```csharp
|
||||||
|
public void Register(ManagedBehaviour component)
|
||||||
|
```
|
||||||
|
Registers a component with the lifecycle system. **Called automatically from `ManagedBehaviour.Awake()`.**
|
||||||
|
|
||||||
|
##### `Unregister(ManagedBehaviour component)`
|
||||||
|
```csharp
|
||||||
|
public void Unregister(ManagedBehaviour component)
|
||||||
|
```
|
||||||
|
Unregisters a component. **Called automatically from `ManagedBehaviour.OnDestroy()`.**
|
||||||
|
|
||||||
|
##### `OnBootCompletionTriggered()`
|
||||||
|
```csharp
|
||||||
|
public void OnBootCompletionTriggered()
|
||||||
|
```
|
||||||
|
Called by `CustomBoot` after bootstrap completes. Broadcasts `OnManagedStart()` to all registered components.
|
||||||
|
|
||||||
|
##### `BeginSceneLoad(string sceneName)`
|
||||||
|
```csharp
|
||||||
|
public void BeginSceneLoad(string sceneName)
|
||||||
|
```
|
||||||
|
Activates scene loading batching mode. Called by `SceneManagerService` when loading a scene.
|
||||||
|
|
||||||
|
##### `BroadcastSceneReady(string sceneName)`
|
||||||
|
```csharp
|
||||||
|
public void BroadcastSceneReady(string sceneName)
|
||||||
|
```
|
||||||
|
Processes batched components and broadcasts `OnSceneReady()` to all components in the scene. Called by `SceneManagerService` after scene load.
|
||||||
|
|
||||||
|
##### `BroadcastSceneUnloading(string sceneName)`
|
||||||
|
```csharp
|
||||||
|
public void BroadcastSceneUnloading(string sceneName)
|
||||||
|
```
|
||||||
|
Broadcasts `OnSceneUnloading()` to all components in the specified scene. Called by `SceneManagerService` before scene unload.
|
||||||
|
|
||||||
|
##### `BroadcastSceneSaveRequested()`
|
||||||
|
```csharp
|
||||||
|
public Dictionary<string, string> BroadcastSceneSaveRequested()
|
||||||
|
```
|
||||||
|
Broadcasts `OnSceneSaveRequested()` to all components with `AutoRegisterForSave == true`. Returns dictionary of SaveId → serialized data.
|
||||||
|
|
||||||
|
##### `BroadcastSceneRestoreRequested(Dictionary<string, string> saveData)`
|
||||||
|
```csharp
|
||||||
|
public void BroadcastSceneRestoreRequested(Dictionary<string, string> saveData)
|
||||||
|
```
|
||||||
|
Distributes save data to components by matching `SaveId`, then broadcasts `OnSceneRestoreCompleted()`.
|
||||||
|
|
||||||
|
##### `BroadcastGlobalSaveRequested()`
|
||||||
|
```csharp
|
||||||
|
public Dictionary<string, string> BroadcastGlobalSaveRequested()
|
||||||
|
```
|
||||||
|
Broadcasts `OnGlobalSaveRequested()` to all components with `AutoRegisterForSave == true`. Returns dictionary of SaveId → serialized data.
|
||||||
|
|
||||||
|
##### `BroadcastGlobalRestoreRequested(Dictionary<string, string> saveData)`
|
||||||
|
```csharp
|
||||||
|
public void BroadcastGlobalRestoreRequested(Dictionary<string, string> saveData)
|
||||||
|
```
|
||||||
|
Distributes save data to components by matching `SaveId`, then broadcasts `OnGlobalLoadCompleted()`.
|
||||||
|
|
||||||
|
##### `BroadcastGlobalSaveStarted()`
|
||||||
|
```csharp
|
||||||
|
public void BroadcastGlobalSaveStarted()
|
||||||
|
```
|
||||||
|
Broadcasts `OnGlobalSaveStarted()` to all components with `AutoRegisterForSave == true`.
|
||||||
|
|
||||||
|
## LifecyclePhase (Enum)
|
||||||
|
|
||||||
|
**Namespace:** `Core.Lifecycle`
|
||||||
|
**Location:** `Assets/Scripts/Core/Lifecycle/LifecycleEnums.cs` → [View Source](../../Assets/Scripts/Core/Lifecycle/LifecycleEnums.cs)
|
||||||
|
|
||||||
|
Defines the different lifecycle phases for documentation and tooling purposes.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public enum LifecyclePhase
|
||||||
|
{
|
||||||
|
ManagedAwake, // During registration (Awake)
|
||||||
|
ManagedStart, // After bootstrap or late registration
|
||||||
|
SceneUnloading, // Before scene unload
|
||||||
|
SceneReady, // After scene load
|
||||||
|
SaveRequested, // Before scene unload (save)
|
||||||
|
RestoreRequested, // After scene load (restore)
|
||||||
|
ManagedDestroy // During OnDestroy
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
522
docs/managed_behavior/use_cases_quick_start.md
Normal file
522
docs/managed_behavior/use_cases_quick_start.md
Normal file
@@ -0,0 +1,522 @@
|
|||||||
|
# ManagedBehaviour System - Quick Start & Use Cases
|
||||||
|
|
||||||
|
**TL;DR:** Inherit from `ManagedBehaviour` instead of `MonoBehaviour`. Override lifecycle hooks instead of Awake/OnDestroy. Get guaranteed initialization order and automatic registration.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Lifecycle Methods Summary](#lifecycle-methods-summary)
|
||||||
|
2. [Quick Reference - Common Use Cases](#quick-reference---common-use-cases)
|
||||||
|
3. [Getting Started Examples](#getting-started-examples)
|
||||||
|
4. [Detailed Use Cases](#detailed-use-cases)
|
||||||
|
5. [Common Patterns](#common-patterns)
|
||||||
|
6. [Migration Checklist](#migration-checklist)
|
||||||
|
7. [Troubleshooting](#troubleshooting)
|
||||||
|
8. [Best Practices](#best-practices)
|
||||||
|
9. [FAQ](#faq)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
**ManagedBehaviour in 3 Sentences:**
|
||||||
|
|
||||||
|
Inherit from `ManagedBehaviour` to get automatic lifecycle management with guaranteed initialization order. Use `OnManagedStart()` to safely access manager singletons, and use built-in save/load hooks for persistence. The framework handles registration and cleanup automatically - you just override the hooks you need.
|
||||||
|
|
||||||
|
**When to Use:**
|
||||||
|
- ✅ Singleton managers
|
||||||
|
- ✅ Components that access managers
|
||||||
|
- ✅ Components that need save/load
|
||||||
|
- ✅ Components that need scene lifecycle events
|
||||||
|
|
||||||
|
**When to Skip:**
|
||||||
|
- ❌ Simple self-contained components
|
||||||
|
- ❌ Third-party code (can't change base class)
|
||||||
|
- ❌ Performance-critical code with no dependencies
|
||||||
|
|
||||||
|
**But can I still use regular MonoBehaviour?!** <br>
|
||||||
|
_Yes!_ Only use ManagedBehaviour when you need its features (manager access, save/load, etc.)
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Lifecycle Methods Summary
|
||||||
|
|
||||||
|
1. **OnManagedAwake()** - Called during Unity's Awake phase; use for **internal setup** and GetComponent calls.
|
||||||
|
2. **OnManagedStart()** - Called after bootstrap completes; **safe to access** manager singletons.
|
||||||
|
3. **OnSceneReady()** - Called after scene finishes loading; use for scene-specific initialization.
|
||||||
|
4. **OnSceneUnloading()** - Called before scene unloads; use for scene cleanup.
|
||||||
|
5. **OnSceneSaveRequested()** - Returns serialized scene-specific data before scene transitions.
|
||||||
|
6. **OnSceneRestoreRequested(data)** - Receives saved scene data after scene loads.
|
||||||
|
7. **OnSceneRestoreCompleted()** - Called after all scene restore operations complete.
|
||||||
|
8. **OnGlobalSaveRequested()** - Returns serialized persistent data when game saves.
|
||||||
|
9. **OnGlobalRestoreRequested(data)** - Receives persistent data on game boot.
|
||||||
|
10. **OnGlobalLoadCompleted()** - Called after all global restore operations complete on boot.
|
||||||
|
11. **OnGlobalSaveStarted()** - Called before save file is written; use for pre-save validation.
|
||||||
|
12. **OnManagedDestroy()** - Called during OnDestroy; use for cleanup and event unsubscription.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Reference - Common Use Cases
|
||||||
|
|
||||||
|
### Access Manager Singleton Safely
|
||||||
|
```csharp
|
||||||
|
internal override void OnManagedStart()
|
||||||
|
{
|
||||||
|
GameManager.Instance.DoSomething(); // Safe - managers guaranteed ready
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create Singleton Manager
|
||||||
|
```csharp
|
||||||
|
private static MyManager _instance;
|
||||||
|
public static MyManager Instance => _instance;
|
||||||
|
|
||||||
|
internal override void OnManagedAwake()
|
||||||
|
{
|
||||||
|
_instance = this;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Save Scene Progress
|
||||||
|
```csharp
|
||||||
|
public override bool AutoRegisterForSave => true;
|
||||||
|
|
||||||
|
internal override string OnSceneSaveRequested()
|
||||||
|
{
|
||||||
|
return JsonUtility.ToJson(myData);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void OnSceneRestoreRequested(string data)
|
||||||
|
{
|
||||||
|
myData = JsonUtility.FromJson<MyData>(data);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Auto-Register as Pausable
|
||||||
|
```csharp
|
||||||
|
public class MyComponent : ManagedBehaviour, IPausable
|
||||||
|
{
|
||||||
|
public override bool AutoRegisterPausable => true;
|
||||||
|
|
||||||
|
public void Pause() { /* pause logic */ }
|
||||||
|
public void Resume() { /* resume logic */ }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scene-Specific Initialization
|
||||||
|
```csharp
|
||||||
|
internal override void OnSceneReady()
|
||||||
|
{
|
||||||
|
FindObjectsInScene();
|
||||||
|
InitializeLevel();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cleanup on Destroy
|
||||||
|
```csharp
|
||||||
|
internal override void OnManagedDestroy()
|
||||||
|
{
|
||||||
|
EventBus.OnSomething -= HandleEvent;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Getting Started Examples
|
||||||
|
|
||||||
|
### 1. Basic Component
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Core.Lifecycle;
|
||||||
|
|
||||||
|
public class MyComponent : ManagedBehaviour
|
||||||
|
{
|
||||||
|
internal override void OnManagedAwake()
|
||||||
|
{
|
||||||
|
// Early initialization (singleton setup, GetComponent)
|
||||||
|
Debug.Log("MyComponent awakened");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void OnManagedStart()
|
||||||
|
{
|
||||||
|
// Late initialization (safe to access managers)
|
||||||
|
Debug.Log("MyComponent started - managers are ready");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Singleton Manager
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Core.Lifecycle;
|
||||||
|
|
||||||
|
public class MyManager : ManagedBehaviour
|
||||||
|
{
|
||||||
|
private static MyManager _instance;
|
||||||
|
public static MyManager Instance => _instance;
|
||||||
|
|
||||||
|
internal override void OnManagedAwake()
|
||||||
|
{
|
||||||
|
_instance = this; // Set singleton early
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void OnManagedStart()
|
||||||
|
{
|
||||||
|
// All other managers are ready here
|
||||||
|
AudioManager.Instance.PlaySound("ManagerReady");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Component with Save/Load
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Core.Lifecycle;
|
||||||
|
|
||||||
|
public class PuzzleComponent : ManagedBehaviour
|
||||||
|
{
|
||||||
|
public override bool AutoRegisterForSave => true;
|
||||||
|
public override string SaveId => "MyPuzzle"; // Custom ID
|
||||||
|
|
||||||
|
private bool _isSolved;
|
||||||
|
|
||||||
|
internal override string OnSceneSaveRequested()
|
||||||
|
{
|
||||||
|
return JsonUtility.ToJson(new { isSolved = _isSolved });
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void OnSceneRestoreRequested(string data)
|
||||||
|
{
|
||||||
|
var saveData = JsonUtility.FromJson<SaveData>(data);
|
||||||
|
_isSolved = saveData.isSolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.Serializable]
|
||||||
|
private class SaveData
|
||||||
|
{
|
||||||
|
public bool isSolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Component with Cleanup
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Core.Lifecycle;
|
||||||
|
|
||||||
|
public class EventSubscriber : ManagedBehaviour
|
||||||
|
{
|
||||||
|
internal override void OnManagedStart()
|
||||||
|
{
|
||||||
|
GameManager.Instance.OnGamePaused += HandlePause;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void OnManagedDestroy()
|
||||||
|
{
|
||||||
|
// Automatic cleanup
|
||||||
|
if (GameManager.Instance != null)
|
||||||
|
GameManager.Instance.OnGamePaused -= HandlePause;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandlePause() { }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Detailed Use Cases
|
||||||
|
|
||||||
|
### Use Case 1: Accessing Singleton Managers Safely
|
||||||
|
|
||||||
|
**Problem:** Accessing `GameManager.Instance` in `Awake()` might fail if GameManager hasn't initialized yet.
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```csharp
|
||||||
|
public class Player : ManagedBehaviour
|
||||||
|
{
|
||||||
|
// ❌ DON'T: Risky in OnManagedAwake (managers may not be ready)
|
||||||
|
internal override void OnManagedAwake()
|
||||||
|
{
|
||||||
|
// var settings = GameManager.Instance.GetSettings(); // RISKY!
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ DO: Safe in OnManagedStart (managers guaranteed ready)
|
||||||
|
internal override void OnManagedStart()
|
||||||
|
{
|
||||||
|
var settings = GameManager.Instance.GetSettings(); // SAFE!
|
||||||
|
ApplySettings(settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Use Case 2: Scene-Specific Initialization
|
||||||
|
|
||||||
|
**Problem:** Need to initialize something after a scene finishes loading.
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```csharp
|
||||||
|
public class LevelManager : ManagedBehaviour
|
||||||
|
{
|
||||||
|
internal override void OnSceneReady()
|
||||||
|
{
|
||||||
|
// Scene is fully loaded, all objects exist
|
||||||
|
FindObjectiveMarkers();
|
||||||
|
SpawnEnemies();
|
||||||
|
PlayLevelMusic();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Use Case 3: Saving Player Progress
|
||||||
|
|
||||||
|
**Problem:** Need to save level progress when transitioning between scenes.
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```csharp
|
||||||
|
public class LevelProgress : ManagedBehaviour
|
||||||
|
{
|
||||||
|
public override bool AutoRegisterForSave => true;
|
||||||
|
public override string SaveId => "LevelProgress";
|
||||||
|
|
||||||
|
private int _checkpointIndex;
|
||||||
|
private float _timeElapsed;
|
||||||
|
|
||||||
|
internal override string OnSceneSaveRequested()
|
||||||
|
{
|
||||||
|
return JsonUtility.ToJson(new
|
||||||
|
{
|
||||||
|
checkpoint = _checkpointIndex,
|
||||||
|
time = _timeElapsed
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void OnSceneRestoreRequested(string data)
|
||||||
|
{
|
||||||
|
var save = JsonUtility.FromJson<SaveData>(data);
|
||||||
|
_checkpointIndex = save.checkpoint;
|
||||||
|
_timeElapsed = save.time;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void OnSceneRestoreCompleted()
|
||||||
|
{
|
||||||
|
// Restore complete, trigger UI update
|
||||||
|
UpdateProgressUI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Use Case 4: Global Persistent Data (Inventory)
|
||||||
|
|
||||||
|
**Problem:** Need to save player inventory across all scenes.
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```csharp
|
||||||
|
public class InventoryManager : ManagedBehaviour
|
||||||
|
{
|
||||||
|
public override bool AutoRegisterForSave => true;
|
||||||
|
public override string SaveId => "Inventory";
|
||||||
|
|
||||||
|
private List<string> _items = new List<string>();
|
||||||
|
|
||||||
|
internal override string OnGlobalSaveRequested()
|
||||||
|
{
|
||||||
|
// Save to global save file (not scene-specific)
|
||||||
|
return JsonUtility.ToJson(new { items = _items });
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void OnGlobalRestoreRequested(string data)
|
||||||
|
{
|
||||||
|
// Restore from global save file on boot
|
||||||
|
var save = JsonUtility.FromJson<SaveData>(data);
|
||||||
|
_items = new List<string>(save.items);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void OnGlobalLoadCompleted()
|
||||||
|
{
|
||||||
|
// All global data loaded, safe to initialize UI
|
||||||
|
RefreshInventoryUI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Use Case 5: Auto-Registering as Pausable
|
||||||
|
|
||||||
|
**Problem:** Component implements `IPausable` and needs to register with `GameManager`.
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```csharp
|
||||||
|
public class AnimatedCharacter : ManagedBehaviour, IPausable
|
||||||
|
{
|
||||||
|
public override bool AutoRegisterPausable => true;
|
||||||
|
|
||||||
|
// IPausable implementation
|
||||||
|
public void Pause()
|
||||||
|
{
|
||||||
|
// Pause animations
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Resume()
|
||||||
|
{
|
||||||
|
// Resume animations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No manual registration needed - automatic!
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Use Case 6: Scene Cleanup
|
||||||
|
|
||||||
|
**Problem:** Need to clean up scene-specific state before transitioning.
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```csharp
|
||||||
|
public class ParticleSpawner : ManagedBehaviour
|
||||||
|
{
|
||||||
|
private List<GameObject> _activeParticles = new List<GameObject>();
|
||||||
|
|
||||||
|
internal override void OnSceneUnloading()
|
||||||
|
{
|
||||||
|
// Clean up before scene unloads
|
||||||
|
foreach (var particle in _activeParticles)
|
||||||
|
{
|
||||||
|
if (particle != null)
|
||||||
|
Destroy(particle);
|
||||||
|
}
|
||||||
|
_activeParticles.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Use Case 7: First-Time vs Restored State
|
||||||
|
|
||||||
|
**Problem:** Need to play intro animation only on first visit, not when restoring from save.
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```csharp
|
||||||
|
public class LevelIntro : ManagedBehaviour
|
||||||
|
{
|
||||||
|
public override bool AutoRegisterForSave => true;
|
||||||
|
|
||||||
|
private bool _hasPlayedIntro;
|
||||||
|
|
||||||
|
internal override string OnSceneSaveRequested()
|
||||||
|
{
|
||||||
|
return JsonUtility.ToJson(new { hasPlayed = _hasPlayedIntro });
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void OnSceneRestoreRequested(string data)
|
||||||
|
{
|
||||||
|
var save = JsonUtility.FromJson<SaveData>(data);
|
||||||
|
_hasPlayedIntro = save.hasPlayed;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void OnSceneRestoreCompleted()
|
||||||
|
{
|
||||||
|
// This fires whether we restored or not
|
||||||
|
if (!_hasPlayedIntro)
|
||||||
|
{
|
||||||
|
PlayIntroAnimation();
|
||||||
|
_hasPlayedIntro = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
### Pattern: Manager Singleton
|
||||||
|
```csharp
|
||||||
|
public class MyManager : ManagedBehaviour
|
||||||
|
{
|
||||||
|
private static MyManager _instance;
|
||||||
|
public static MyManager Instance => _instance;
|
||||||
|
|
||||||
|
internal override void OnManagedAwake()
|
||||||
|
{
|
||||||
|
_instance = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern: Event Subscription
|
||||||
|
```csharp
|
||||||
|
internal override void OnManagedStart()
|
||||||
|
{
|
||||||
|
EventBus.OnSomething += HandleEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void OnManagedDestroy()
|
||||||
|
{
|
||||||
|
EventBus.OnSomething -= HandleEvent;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern: Save/Restore
|
||||||
|
```csharp
|
||||||
|
public override bool AutoRegisterForSave => true;
|
||||||
|
|
||||||
|
internal override string OnSceneSaveRequested()
|
||||||
|
{
|
||||||
|
return JsonUtility.ToJson(myData);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void OnSceneRestoreRequested(string data)
|
||||||
|
{
|
||||||
|
myData = JsonUtility.FromJson<MyData>(data);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern: Custom SaveId
|
||||||
|
```csharp
|
||||||
|
// For singletons or special cases
|
||||||
|
public override string SaveId => "PlayerInventory";
|
||||||
|
|
||||||
|
// For scene-specific instances
|
||||||
|
public override string SaveId => $"{gameObject.scene.name}/MySpecialObject";
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "NullReferenceException when accessing Manager.Instance"
|
||||||
|
**Problem:** Accessing singleton in `OnManagedAwake()`
|
||||||
|
**Solution:** Move to `OnManagedStart()` where managers are guaranteed ready
|
||||||
|
|
||||||
|
### "My SaveId is colliding with another component"
|
||||||
|
**Problem:** Two components have same GameObject name and type
|
||||||
|
**Solution:** Override `SaveId` property with unique value
|
||||||
|
|
||||||
|
### "My component isn't receiving lifecycle callbacks"
|
||||||
|
**Problem:** Not inheriting from `ManagedBehaviour`
|
||||||
|
**Solution:** Ensure class inherits `ManagedBehaviour` (not plain `MonoBehaviour`)
|
||||||
|
|
||||||
|
### "Save data isn't persisting"
|
||||||
|
**Problem:** `AutoRegisterForSave` is false
|
||||||
|
**Solution:** Set `public override bool AutoRegisterForSave => true;`
|
||||||
|
|
||||||
|
### "OnSceneRestoreCompleted isn't firing"
|
||||||
|
**Problem:** Missing implementation
|
||||||
|
**Solution:** Override the method even if just logging for now
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Quick Links:**
|
||||||
|
- [Technical Reference](01_technical_reference.md) - Complete API documentation
|
||||||
|
- [Architecture Overview](02_architecture_overview.md) - System design and principles
|
||||||
|
|
||||||
|
|
||||||
BIN
docs/media/Scene_transition_flow.png
Normal file
BIN
docs/media/Scene_transition_flow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 287 KiB |
BIN
docs/media/boot_sequence.png
Normal file
BIN
docs/media/boot_sequence.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 239 KiB |
BIN
docs/media/class_diagram.png
Normal file
BIN
docs/media/class_diagram.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 252 KiB |
BIN
docs/media/component_lifecycle.png
Normal file
BIN
docs/media/component_lifecycle.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 219 KiB |
Reference in New Issue
Block a user