Game Save and Load System Implementation
We implement production-ready save and load systems for Unity games — replacing fragile PlayerPrefs workflows with structured, versioned data pipelines. PlayerPrefs.SetFloat("health", 100) works for prototypes, but for production it is a dead end. When save data grows to dozens of variables, PlayerPrefs becomes an unstructured dump: no versioning, no multi-slot support, no corruption protection. Migrating from PlayerPrefs to a proper save system mid-development is painful and risky, often breaking existing player data. Our implementation delivers a complete, battle-tested save architecture from day one — including atomic writes, async serialization, version migration, and optional cloud sync.
What Save System Must Do
A solid, production-ready save system must handle at least four critical requirements from the first public build:
- Multiple save slots with metadata (date, character name, level, screenshot)
- Atomic write: file is either fully written or not written—intermediate crash doesn't corrupt
- Versioning: old saves migrate on game update, don't break
- Async write: saving doesn't freeze game for 200ms
Architecture: ISaveable and SaveManager
Pattern: each component wanting to save implements ISaveable interface:
public interface ISaveable
{
string SaveId { get; }
object CaptureState();
void RestoreState(object state);
}
SaveManager on save finds all ISaveable on scene (via FindObjectsOfType or registration), calls CaptureState() on each, collects result into Dictionary<string, object>, serializes and writes to disk. On load—reverse process.
SaveId is unique string for each component. GUID generated in inspector via [SerializeField] private string _saveId. Important: don't use scene object name as ID—not unique and can change.
Serialization: JSON vs Binary
JSON (Newtonsoft.Json) is readable, easy to debug, compatible across platforms. Downsides: larger file, slightly slower, need custom converters for Unity types (Vector3, Quaternion, Color). JsonConvert.SerializeObject(data, Formatting.None) with custom UnityTypeConverter—working approach.
BinaryFormatter is built-in, fast, compact. But: deprecated in .NET 5+, has security vulnerabilities (not critical for offline games). Not recommended for new projects.
MessagePack-CSharp is binary format with JSON performance and without BinaryFormatter problems. Good choice for mobile games with large data volumes.
Save file path: Application.persistentDataPath + "/saves/slot_{index}.sav". persistentDataPath is guaranteed writable on all platforms (iOS, Android, PC, Console).
Atomic Write and Corruption Protection
Direct file overwrite via File.WriteAllText(path, json) can leave file invalid if crash during write. Atomic write:
- Write data to temp file
slot_0.sav.tmp - If successful—rename
File.Move(tmpPath, finalPath)(atomic on most OS) - Rename old file to
slot_0.sav.bakbeforehand—backup copy
On load: if main file not found or invalid—try .bak. This elementary protection saves thousands of support hours post-release.
Async Save
Serializing 5 MB JSON synchronously—50–200ms delay on mid-tier PC, worse on mobile. Solution: async/await with File.WriteAllTextAsync():
public async Task SaveAsync(int slot)
{
var data = CollectSaveData();
string json = JsonConvert.SerializeObject(data);
await File.WriteAllTextAsync(GetSavePath(slot), json);
}
In Unity async Task methods work correctly with ConfigureAwait(false) for background threads. UI save indicator shows before call, hides in finally block.
Versioning and Migration
Each save file contains "saveVersion": 3. On load, version compared with currentSaveVersion. If versions differ—run migrator chain:
ISaveMigrator[] migrators = {
new SaveMigratorV1ToV2(),
new SaveMigratorV2ToV3()
};
Each migrator knows how to update JObject from its version to next. This allows save format updates without player data loss. Without versioning, first game update changing data structure invalidates all existing saves.
Autosave and Checkpoint System
Autosave via InvokeRepeating("AutoSave", 300f, 300f)—every 5 minutes to special autosave slot. Checkpoint save: on trigger zone entry event published OnCheckpointReached, SaveManager saves to checkpoint slot without UI.
Critical: don't save during combat or loaded scene—choose save moment so background write thread doesn't compete with gameplay CPU peak. isSafeToSave flag clears during intensive scenes.
Timeline Guidelines
| Scale | Components | Timeframe |
|---|---|---|
| Simple | JSON, single slot, no versioning | 2–4 days |
| Basic | ISaveable pattern, multiple slots, atomic write | 1–2 weeks |
| Full | Async, versioning, migration, cloud sync | 3–5 weeks |
| Cloud saves | + Unity Cloud Save / Steam Cloud integration | +1–2 weeks |
Process
Start writing SaveManager with test in isolation (Unity Test Runner): save data, load, check identity. Then integrate ISaveable into existing components one by one. Simulate write crash during testing—deliberately interrupt save process and verify data didn't corrupt. Cloud sync implemented last — only after stable local saving. We deliver the complete save system package, everything included from ISaveable architecture to cloud sync integration. Contact us to discuss your project scope and timeline.





