SaveSystem
The SaveSystem handles persistent storage: how many save slots the player gets, what format saves use on disk, whether they’re encrypted, and when autosave fires.
Open it from Inspector → Systems → SaveSystem → Open.
Schema
Section titled “Schema”| Field | Type | What it does |
|---|---|---|
slotCount | int (1-16) | Manual save slot count. The bundles default to 3. |
format | enum | json (portable), binary (compact), engine-native (Unity ScriptableObject / Godot Resource), encrypted-json (AES, key derived from a per-install salt). |
encrypt | bool | When format is json, toggles encryption on the side. |
autosave.enabled | bool | Master toggle for automatic saves. |
autosave.debounceMs | int | Minimum ms between two autosave writes. Default 30000 (30s). Prevents save-thrashing on rapid state changes. |
autosave.milestoneEvents | string array | EventBus event names that immediately trigger an autosave. Default: level_loaded, checkpoint_reached, boss_defeated. |
Where saves live
Section titled “Where saves live”The default storage backend is localStorage for web bundles. Each slot is a separate localStorage key. The Top-down RPG bundle uses a single slot keyed at <bundle-id>:save:default; multi-slot games use <bundle-id>:save:1, <bundle-id>:save:2, etc.
For engine-native bundles (Unity, Godot), the SaveSystem writes to the engine’s standard persistent paths (Application.persistentDataPath for Unity, user://saves/ for Godot).
Autosave triggers
Section titled “Autosave triggers”Two paths fire an autosave:
- Debounce after any mutation. When game state changes (XP, inventory, quest progress), the SaveSystem schedules an autosave for
debounceMslater. Further mutations reset the timer. Once the timer fires, the actual write happens. - Milestone events. When any of the listed
milestoneEventsfires on the EventBus, autosave is immediate (no debounce). Use this for moments the player would be devastated to lose: completing a level, reaching a checkpoint, defeating a boss.
The Top-down RPG bundle adds quest_complete to its milestone list so the fetch quest’s resolution is committed instantly.
Format choices
Section titled “Format choices”jsonis the default. Human-readable, easy to debug, easy to migrate (just write amigrate(v1, v2)function). Use unless you have a reason not to.binaryis more compact but opaque. Useful for save-heavy games where storage cost matters (long sessions, lots of fine-grained state). Pay the debugging cost.engine-nativeis the right choice for Unity / Godot when you want save data to integrate with the engine’s serialization. Less portable but more idiomatic.encrypted-jsonlightly obfuscates saves. Won’t stop a motivated tamper, but raises the bar above casual editing. Use when you have leaderboards or competitive metas.
Common patterns
Section titled “Common patterns”Pure autosave, no manual slots: set slotCount = 1, leave autosave on, optionally add custom milestone events. Players never see a save menu; the game just remembers where they were.
Multiple manual slots plus autosave: set slotCount = 3 and treat slot 0 as the autosave. Players can manually save to slots 1 or 2 at the menu, but the live game writes to slot 0.
Roguelike, no saves: set autosave.enabled = false, ignore slots. The game serializes nothing to disk; runs are ephemeral.
Editing the config by hand
Section titled “Editing the config by hand”[systems.SaveSystem.config]slotCount = 3format = "json"encrypt = false
[systems.SaveSystem.config.autosave] enabled = true debounceMs = 30000 milestoneEvents = ["level_loaded", "checkpoint_reached", "boss_defeated"]Validation: slotCount is clamped to 1..16, format must be one of the four supported values.