How-To: Build a Save/Load Menu
Goal: build a menu that lists existing save slots with their metadata, and can start a new game, load a slot, or delete one.
UCrimsonSaveGameManagerSubsystem; the UCrimsonSaveFacade Blueprint library offers the same actions in one call each.1. List the slots
GetAllSaveSlotHeaders scans the save directory and returns one UCrimsonSaveGameHeader per manual slot (auto/quick-save slots are excluded; list those with GetRotatingSaveSlotIndices, below). Read each header's metadata to build a row.
In the save-menu widget: Event Construct -> Get Crimson Save Game Manager -> Get All Save Slot Headers -> For Each Loop. Get All Save Slot Headers reads the disk, so it has exec pins and belongs on the exec chain rather than dangling as a pure read.
In the loop body, Create Widget for your row, then drag off the loop's Array Element - a Crimson Save Game Header object reference - to read Display Name (Text), Slot Type (ECrimsonSaveSlotType), Save Date Time (DateTime), and Play Time (Timespan) into it. Finish with Add Child onto your list panel. All four are read-only variables on the header, so each is a plain getter node off that pin.
Quick and auto saves. These live in their own rotating index ranges, so GetAllSaveSlotHeaders leaves them out. GetRotatingSaveSlotIndices returns the slot indices of one range (AutoSave or QuickSave); pass each index to GetSaveSlotHeader for its metadata and to RequestLoadFromSlot to load it. Each header's SlotType and DisplayName are the ones the quick or auto save was made with.
In the same graph: Get Crimson Save Game Manager -> Get Rotating Save Slot Indices (Slot Type = Quick Save or Auto Save) -> For Each Loop. In the loop body, call Get Save Slot Header with Slot Index = the loop's Array Element and build the row from that header exactly as above. Store the index on the row: it is the Slot Index that Request Load From Slot takes.
SlotType QuickSave and the display names you passed to the quick save.2. New game, load, delete
Bind the menu buttons to the slot actions. UCrimsonSaveFacade wraps each in a single call; the raw subsystem exposes the same operations.
Bind one node per button: On Clicked (NewGameButton) -> Start New Game (Slot Index, Character Name from your text box); On Clicked (LoadButton) -> Load Game (Slot Index = the selected row's slot); On Clicked (DeleteButton) -> Delete Slot (Slot Index). All three are Crimson Save Facade statics - search the node name in the graph, take it from the Crimson|SaveSystem category, and note there is no manager node to fetch first and no World Context Object pin to fill in a widget graph.
Prefer the raw subsystem? The same three actions are Get Crimson Save Game Manager -> Request New Game Save / Request Load From Slot / Request Delete Slot. Request New Game Save is the one that differs: it hands back a bool and an Out Error Message you can branch on, where Start New Game returns only the bool.
RequestDeleteSlot removes every file in the slot directory. Confirm with the player before calling it.3. Refresh the list when it changes
Saves and loads are event-driven. Bind OnSaveSlotListChanged to rebuild the list after a slot is created or deleted, and OnLoadComplete to know when a load has actually finished before switching screens.
SaveMgr->OnSaveSlotListChanged.AddDynamic(this, &UMySaveMenu::Rebuild);SaveMgr->OnLoadComplete.AddDynamic(this, &UMySaveMenu::HandleLoadComplete);
UCrimsonSaveGameHeader:| Field | Type | Use |
|---|---|---|
DisplayName | FText | User-facing slot name (e.g. the character name entered at new game). |
SlotType | ECrimsonSaveSlotType | Manual / AutoSave / QuickSave - label or group rows by the kind of save. |
SaveDateTime | FDateTime | When the save was written. |
PlayTime | FTimespan | Total playtime accumulated in the slot. |
ThumbnailData | TArray<uint8> | Optional screenshot captured at save time. |
See also
- How-To: Handle Save/Load Errors - resilient feedback when a slot is corrupt
- How-To: Custom Fragments & Versioning - add custom header fields (chapter, portrait)
- API Reference -> Save Manager Subsystem - the full slot/header API