112 lines
3.4 KiB
Markdown
112 lines
3.4 KiB
Markdown
|
|
# Save System Architecture
|
||
|
|
|
||
|
|
**Project:** AppleHills
|
||
|
|
**Date:** November 3, 2025
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Critical Decision: All Save Data is Level-Specific
|
||
|
|
|
||
|
|
**IMPORTANT:** In AppleHills, **ALL save data is level-specific**. There is no global persistent save data that carries across all levels.
|
||
|
|
|
||
|
|
### What This Means
|
||
|
|
|
||
|
|
- ❌ **DO NOT use ISaveParticipant pattern** for new components
|
||
|
|
- ✅ **DO use OnSaveRequested() / OnRestoreRequested() lifecycle hooks** instead
|
||
|
|
- ✅ All save/load operations are tied to the current level/scene
|
||
|
|
|
||
|
|
### Migration Impact
|
||
|
|
|
||
|
|
For existing components that implement ISaveParticipant:
|
||
|
|
- **Remove ISaveParticipant interface** implementation
|
||
|
|
- **Remove SaveLoadManager.RegisterParticipant()** calls
|
||
|
|
- **Add OnSaveRequested()** override to save level-specific state
|
||
|
|
- **Add OnRestoreRequested()** override to restore level-specific state
|
||
|
|
- **Use SaveLoadManager's existing save/load system** within these hooks
|
||
|
|
|
||
|
|
### Examples
|
||
|
|
|
||
|
|
#### ❌ OLD Pattern (Don't use)
|
||
|
|
```csharp
|
||
|
|
public class PuzzleManager : MonoBehaviour, ISaveParticipant
|
||
|
|
{
|
||
|
|
void Awake()
|
||
|
|
{
|
||
|
|
BootCompletionService.RegisterInitAction(() => {
|
||
|
|
SaveLoadManager.Instance.RegisterParticipant(this);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
public string GetSaveId() => "PuzzleManager";
|
||
|
|
|
||
|
|
public string Save()
|
||
|
|
{
|
||
|
|
return JsonUtility.ToJson(puzzleState);
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Restore(string data)
|
||
|
|
{
|
||
|
|
puzzleState = JsonUtility.FromJson<PuzzleState>(data);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### ✅ NEW Pattern (Use this)
|
||
|
|
```csharp
|
||
|
|
public class PuzzleManager : ManagedBehaviour
|
||
|
|
{
|
||
|
|
protected override int ManagedAwakePriority => 120;
|
||
|
|
|
||
|
|
protected override void OnSaveRequested()
|
||
|
|
{
|
||
|
|
// Save level-specific puzzle state
|
||
|
|
string saveData = JsonUtility.ToJson(puzzleState);
|
||
|
|
SaveLoadManager.Instance.SaveLevelData("PuzzleManager", saveData);
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override void OnRestoreRequested()
|
||
|
|
{
|
||
|
|
// Restore level-specific puzzle state
|
||
|
|
string saveData = SaveLoadManager.Instance.LoadLevelData("PuzzleManager");
|
||
|
|
if (!string.IsNullOrEmpty(saveData))
|
||
|
|
{
|
||
|
|
puzzleState = JsonUtility.FromJson<PuzzleState>(saveData);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Components Affected by This Decision
|
||
|
|
|
||
|
|
All components that currently use ISaveParticipant:
|
||
|
|
1. PuzzleManager - puzzle state per level
|
||
|
|
2. PlayerTouchController - player position/state per level
|
||
|
|
3. FollowerController - follower state per level
|
||
|
|
4. CardSystemManager - card collection per level
|
||
|
|
5. SaveableInteractable - interactable state per level
|
||
|
|
6. AppleMachine - apple machine state per level
|
||
|
|
|
||
|
|
**All of these will be migrated to use OnSaveRequested/OnRestoreRequested instead of ISaveParticipant.**
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## SaveLoadManager Integration
|
||
|
|
|
||
|
|
The SaveLoadManager will continue to work as before, but components will interact with it through lifecycle hooks:
|
||
|
|
|
||
|
|
### Lifecycle Flow
|
||
|
|
1. **Before scene unloads:** `LifecycleManager.BroadcastSaveRequested(sceneName)`
|
||
|
|
- All ManagedBehaviours in that scene get `OnSaveRequested()` called
|
||
|
|
- Each component saves its level-specific data via SaveLoadManager
|
||
|
|
2. **SaveLoadManager.Save()** called to persist all level data
|
||
|
|
3. **Scene unloads**
|
||
|
|
4. **New scene loads**
|
||
|
|
5. **After scene loads:** `LifecycleManager.BroadcastRestoreRequested(sceneName)`
|
||
|
|
- All ManagedBehaviours in that scene get `OnRestoreRequested()` called
|
||
|
|
- Each component restores its level-specific data from SaveLoadManager
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**End of Document**
|
||
|
|
|