Files
UE5-Modular-Game-Framework/docs/blueprints/05-saveload/36_I_Persistable.md

107 lines
4.4 KiB
Markdown

# I_Persistable — Blueprint Interface
**File:** [`Content/Framework/Save/I_Persistable`](Content/Framework/Save/I_Persistable.uasset)
> **⚡ C++ Status: Full Implementation** — Defined in `Source/PG_Framework/Public/Core/I_InterfaceLibrary.h` as `BlueprintNativeEvent`. **Do NOT create a Blueprint Interface asset.** On any actor that needs save/load: Class Settings → Interfaces → Add → `UPersistable`. Override `On Save`, `On Load`, `Get Save Tag`, `Needs Save`. See `docs/developer/cpp-integration-guide.md`.
>
> ---
**Purpose:** Interface for any Actor or Component in the world that needs to save or restore per-instance state. Implemented by doors, containers, lootable objects, hidden collectibles, puzzle elements, destructibles, and any other persistent world actor.
**Depends On:** None
**Used By:** [`SS_SaveManager`](35_SS_SaveManager.md), All persistent world actors
---
## Interface Functions
| Function | Inputs | Outputs | Description |
|----------|--------|---------|-------------|
| `CollectState` | — | `S_WorldObjectState` | The implementer packs its current state into a generic key-value struct |
| `RestoreState` | StateData: `S_WorldObjectState` | — | The implementer unpacks the struct and reapplies its state |
| `GetPersistenceTag` | — | GameplayTag | Returns the unique gameplay tag that identifies this instance in the save manifest |
| `ShouldPersist` | — | Bool | Returns whether this actor should be serialised (e.g., false for temporary actors spawned via FX) |
| `OnPreSave` | — | — | Called before the save operation begins; used to flush transient state |
| `OnPostLoad` | — | — | Called after all state has been distributed; used to trigger animations, re-enable physics, etc. |
---
## Required Variable (on implementer)
| Name | Type | Description |
|------|------|-------------|
| `PersistenceTag` | GameplayTag | Set in Construction Script or Defaults. Must be unique within the level. Convention: `Save.<LevelName>.<ActorClassName>.<Index>` |
---
## Implementation Pattern
```mermaid
flowchart LR
A[SS_SaveManager.CollectWorldState] --> B[Iterate registered I_Persistable actors]
B --> C[Call actor.ShouldPersist]
C --> D{Should persist?}
D -->|No| E[Skip]
D -->|Yes| F[Call actor.OnPreSave]
F --> G[Call actor.CollectState]
G --> H[Store in WorldStateDelta map]
H --> I[Save to disk]
J[SS_SaveManager.DistributeWorldState] --> K[Iterate WorldStateDelta entries]
K --> L[Find actor by ObjectTag]
L --> M{Found?}
M -->|No| N[Log warning]
M -->|Yes| O[Call actor.RestoreState]
O --> P[Call actor.OnPostLoad]
P --> Q[Continue]
```
---
## Example: BP_DoorWithState implements I_Persistable
```cpp
// On CollectState
State.bDestroyed = bIsDestroyed;
State.CustomData.Add("bOpen", bIsOpen);
State.CustomData.Add("bLocked", bIsLocked);
State.CustomData.Add("Health", Health);
State.CustomData.Add("PuzzleSolved", bPuzzleSolved);
return State;
// On RestoreState(State)
bIsDestroyed = State.bDestroyed;
bIsOpen = State.CustomData["bOpen"].AsBool();
bIsLocked = State.CustomData["bLocked"].AsBool();
Health = State.CustomData["Health"].AsFloat();
bPuzzleSolved = State.CustomData["PuzzleSolved"].AsBool();
```
---
## PersistenceTag Convention
| Tag Pattern | Example | Used For |
|-------------|---------|----------|
| `Save.LevelName.ActorClass.Index` | `Save.Hospital.DoorHeavy.03` | Level-dense unique ID |
| `Save.Persistent.ActorName` | `Save.Persistent.MansionKey` | Unique collectibles that span levels |
| `Save.Global.FlagName` | `Save.Global.EndingUnlocked` | Cross-save narrative flags |
---
## Communication Matrix
| Target System | Method | Why |
|---------------|--------|------|
| `SS_SaveManager` | Interface call | State collection and distribution |
| `SS_SaveManager` | Register/Unregister | Lifecycle tracking (BeginPlay/EndPlay) |
---
## Reuse Notes
- Keep `CustomData` entries simple: only Bool, Float, Int, String, or Name values
- For complex state (arrays of structs), serialise to a Json string and store as a single `CustomData` entry
- Use `OnPostLoad` to re-trigger timelines or timelines that drive animations (e.g., door opening animation after restoring closed state)
- Implement `ShouldPersist` to return `false` for actors that are editor-placed but runtime-destroyed without state significance (e.g., particle emitters)
- All interface functions are BlueprintImplementableEvent — no logic required in the interface itself