4.0 KiB
4.0 KiB
I_Persistable — Blueprint Interface
File: Content/Framework/Save/I_Persistable
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, 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
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
// 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
CustomDataentries 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
CustomDataentry - Use
OnPostLoadto re-trigger timelines or timelines that drive animations (e.g., door opening animation after restoring closed state) - Implement
ShouldPersistto returnfalsefor 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