Реализация системы сохранения и загрузки данных игр

Наша компания по разработке видеоигр ведет независимые проекты, совместно с клиентом создает игры и оказывает дополнительные операционные услуги. Опыт нашей команды позволяет нам охватить все игровые платформы и разработать потрясающий продукт, соответствующий видению клиента и предпочтениям игроков.

От иммерсивных приложений до игровых миров и 3D-сцен

Наша выделенная команда для VR/AR/MR-разработки, Unity-продакшна и 3D-моделирования и анимации с собственными кейсами и презентациями.

Посетить персонализированный сайт
Показано 1 из 1 услугВсе 242 услуг
Реализация системы сохранения и загрузки данных игр
Средняя
от 2 рабочих дней до 1 недели
Часто задаваемые вопросы
Наши компетенции
Какие этапы разработки игры?
Последние работы
  • image_games_mortal_motors_495_0.webp
    Разработка игры для компании Mortal Motors
    671
  • image_games_a_turnbased_strategy_game_set_in_a_fantasy_setting_with_fire_and_sword_603_0.webp
    Пошаговая стратегия в фэнтези сеттинге With Fire And Sword
    860
  • image_games_second_team_604_0.webp
    Разработка игры для компании Second term
    490
  • image_games_phoenix_ii_606_0.webp
    3D-анимация — тизер для игры phoenix 2.
    533

Реализация системы сохранения и загрузки данных игр

PlayerPrefs.SetFloat("health", 100) работает для прототипа. Для продакшена — это тупик. Когда объём сохраняемых данных вырастает до десятков переменных, PlayerPrefs превращается в неструктурированную свалку без версионирования, без возможности нескольких слотов и без защиты от корруптации. Перейти с PlayerPrefs на нормальную систему в середине разработки — болезненная задача.

Что должна уметь система сохранения

Минимальный production-ready набор:

  • Несколько слотов сохранения с метаданными (дата, имя персонажа, уровень, скриншот)
  • Атомарная запись: файл либо записан полностью, либо не записан вообще (промежуточный краш не корруптирует данные)
  • Версионирование: при обновлении игры старые сохранения должны мигрировать, а не ломаться
  • Асинхронная запись: сохранение не должно фризить игру на 200ms

Архитектура: ISaveable и SaveManager

Паттерн: каждый компонент, который хочет сохраняться, реализует интерфейс ISaveable:

public interface ISaveable
{
    string SaveId { get; }
    object CaptureState();
    void RestoreState(object state);
}

SaveManager при сохранении находит все ISaveable на сцене (через FindObjectsOfType или регистрацию), вызывает CaptureState() у каждого, собирает результат в Dictionary<string, object>, сериализует и пишет на диск. При загрузке — обратный процесс.

SaveId — уникальная строка для каждого компонента. Используется GUID, генерируемый в инспекторе через [SerializeField] private string _saveId. Важно не использовать имя объекта сцены как ID: оно не уникально и может измениться.

Сериализация: JSON vs Binary

JSON (Newtonsoft.Json) — читаем, легко дебажится, совместим с разными платформами. Минусы: больше объём файла, немного медленнее, нужны кастомные конвертеры для типов Unity (Vector3, Quaternion, Color). JsonConvert.SerializeObject(data, Formatting.None) с кастомным UnityTypeConverter — рабочий подход.

BinaryFormatter — Unity-встроенный, быстрый, компактный. Но: deprecated в .NET 5+, имеет уязвимости безопасности (не критично для offline игр), нечитаем для дебага. Для новых проектов не рекомендуется.

MessagePack-CSharp — бинарный формат с производительностью лучше JSON и без проблем BinaryFormatter. Хороший выбор для мобильных игр с большими объёмами данных.

Путь к файлу сохранения: Application.persistentDataPath + "/saves/slot_{index}.sav". persistentDataPath гарантированно доступен для записи на всех платформах (iOS, Android, PC, Console).

Атомарная запись и защита от корруптации

Прямая перезапись файла File.WriteAllText(path, json) может оставить файл в невалидном состоянии при крэше в момент записи. Атомарная запись:

  1. Записать данные во временный файл slot_0.sav.tmp
  2. Если запись успешна — переименовать File.Move(tmpPath, finalPath) (атомарная операция на большинстве ОС)
  3. Старый файл предварительно переименовать в slot_0.sav.bak — резервная копия

При загрузке: если основной файл не найден или невалиден — попробовать .bak. Это элементарная защита, которая экономит тысячи часов поддержки после релиза.

Асинхронное сохранение

Сериализация 5 МБ JSON синхронно — это 50–200ms задержки на среднем PC, на мобильных устройствах хуже. Решение: async/await с File.WriteAllTextAsync():

public async Task SaveAsync(int slot)
{
    var data = CollectSaveData();
    string json = JsonConvert.SerializeObject(data);
    await File.WriteAllTextAsync(GetSavePath(slot), json);
}

В Unity async Task методы работают корректно с ConfigureAwait(false) для фоновых потоков. UI индикатор сохранения показывается до вызова, скрывается в finally блоке.

Версионирование и миграция

Каждый файл сохранения содержит "saveVersion": 3. При загрузке версия сравнивается с currentSaveVersion. Если версии не совпадают — запускается цепочка мигреторов:

ISaveMigrator[] migrators = {
    new SaveMigratorV1ToV2(),
    new SaveMigratorV2ToV3()
};

Каждый мигратор знает как обновить JObject от своей версии к следующей. Это позволяет обновлять формат сохранений без потери данных игроков. Без версионирования первое же обновление игры с изменением структуры данных инвалидирует все существующие сохранения.

Автосохранение и checkpoint система

Автосохранение через InvokeRepeating("AutoSave", 300f, 300f) — каждые 5 минут в специальный autosave слот. Checkpoint-сохранение: при входе в триггерную зону публикуется событие OnCheckpointReached, SaveManager сохраняет в checkpoint-слот без UI.

Критично: не сохранять в момент боя или нагруженной сцены — выбирать момент сохранения так, чтобы фоновый поток записи не конкурировал с пиком CPU геймплея. Флаг isSafeToSave снимается на время интенсивных сцен.

Ориентировочные сроки

Масштаб Состав Срок
Простой JSON, один слот, без версионирования 2–4 дня
Базовый ISaveable паттерн, несколько слотов, атомарная запись 1–2 недели
Полный Async, версионирование, миграция, cloud sync 3–5 недель
С облачными сохранениями + Unity Cloud Save / Steam Cloud интеграция +1–2 недели

Процесс

Сначала пишем SaveManager с тестом в изоляции (Unity Test Runner): сохранить данные, загрузить, проверить идентичность. Потом интегрируем ISaveable в существующие компоненты по одному. Имитируем краш записи во время тестирования — специально прерываем процесс сохранения и проверяем, что данные не корруптировались. Cloud sync реализуется последним — только после стабильной работы локального сохранения.