Game Save and Load System Implementation

Our video game development company runs independent projects, jointly creates games with the client and provides additional operational services. Expertise of our team allows us to cover all gaming platforms and develop an amazing product that matches the customer’s vision and players preferences.
Showing 1 of 1 servicesAll 242 services
Game Save and Load System Implementation
Medium
from 2 business days to 1 week
FAQ
Our competencies
What are the stages of Game Development?
Latest works
  • image_games_mortal_motors_495_0.webp
    Game development for Mortal Motors
    670
  • image_games_a_turnbased_strategy_game_set_in_a_fantasy_setting_with_fire_and_sword_603_0.webp
    A turn-based strategy game set in a fantasy setting, With Fire and Sword
    860
  • image_games_second_team_604_0.webp
    Game development for the company Second term
    490
  • image_games_phoenix_ii_606_0.webp
    3D animation - teaser for the game Phoenix 2.
    533

Game Save and Load System Implementation

PlayerPrefs.SetFloat("health", 100) works for prototypes. For production—it's a dead end. When save data volume grows to dozens of variables, PlayerPrefs becomes an unstructured dump without versioning, without multiple slot support, and without corruption protection. Migrating from PlayerPrefs to a proper system mid-development is painful.

What Save System Must Do

Minimal production-ready set:

  • 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:

  1. Write data to temp file slot_0.sav.tmp
  2. If successful—rename File.Move(tmpPath, finalPath) (atomic on most OS)
  3. Rename old file to slot_0.sav.bak beforehand—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.