Перейти к основному содержимому

Система сохранений

MCE использует систему сохранений на основе JSON со слотами, версионированием и расширяемостью. Система сохранений фиксирует всё состояние игры -- ростер, инвентарь, позицию, значки, прогресс квестов и игровые переменные.

Архитектура

Система сохранений состоит из нескольких компонентов:

КомпонентНазначение
SavegameManagerОркестрирует операции сохранения/загрузки
SavableObjectБазовый класс для любого объекта, нуждающегося в сохранении данных
MCESavesSerializerОбрабатывает JSON-сериализацию и десериализацию
GameVariablesГлобальное хранилище переменных (булевы, целые, строки)
GameVariableСсылка на отдельную переменную
GameVariableHolderMonoBehaviour-обёртка для игровых переменных
SavegameInstallerИнсталлер Zenject для системы сохранений

SavableGlobalGameData

Основной контейнер данных сохранения -- SavableGlobalGameData (компонент на GameObject GlobalGameData). Он хранит:

ДанныеОписание
Позиция игрокаТекущая сцена, координаты тайла, направление взгляда
Ростер монстровВсе монстры отряда с полным состоянием (вид, уровень, IVs, EVs, природа, приёмы, PP, HP, статус, дружба и т.д.)
Хранилище ПКМонстры в ячейках
ИнвентарьВсе предметы и их количество
ДеньгиВалюта игрока
ЗначкиЗаработанные значки залов
MonsterDexОтслеживание увиденных/пойманных по видам
Игровые переменныеВсе значения GameVariable (используемые CommandGraph)
Прогресс квестовАктивные квесты, выполненные цели, полученные награды
Время игрыОбщее время игры

Слоты сохранения

MCE поддерживает несколько слотов сохранения. Конфигурация по умолчанию предоставляет 3 слота, но это настраивается.

Сохранение

// Через SDK (все уровни)
engine.SaveSystem.Save(slotIndex); // 0, 1, or 2

// Или через SavegameManager напрямую (уровень Source)
savegameManager.Save(slotIndex);

Загрузка

// Через SDK (все уровни)
engine.SaveSystem.Load(slotIndex);

// Сначала проверьте наличие данных в слоте
if (engine.SaveSystem.HasSaveData(slotIndex))
{
engine.SaveSystem.Load(slotIndex);
}

Удаление

engine.SaveSystem.DeleteSave(slotIndex);

Расположение файлов сохранения

Файлы сохранения хранятся через Application.persistentDataPath Unity:

ПлатформаПуть
Windows%USERPROFILE%/AppData/LocalLow/<CompanyName>/<ProductName>/
macOS~/Library/Application Support/<CompanyName>/<ProductName>/
Linux~/.config/unity3d/<CompanyName>/<ProductName>/
AndroidВнутреннее хранилище (приватное для приложения)
iOSПесочница приложения

Каждый слот сохраняется как отдельный JSON-файл: save_0.json, save_1.json, save_2.json.

Версионирование сохранений

MCE включает версионирование сохранений для обработки обновлений между версиями игры:

  • Каждый файл сохранения содержит номер версии.
  • При загрузке более старого сохранения MCESavesSerializer применяет шаги миграции для обновления структуры данных.
  • Это предотвращает повреждение сохранений при добавлении новых полей или изменении форматов данных между обновлениями.
Имена сериализуемых полей

Никогда не переименовывайте сериализуемые поля в классах, связанных с сохранениями, без предоставления миграции. Если вы переименуете поле, существующие файлы сохранений потеряют эти данные при загрузке. Используйте [FormerlySerializedAs("oldName")] для переименования полей.

GameVariables

Система GameVariables -- глобальное хранилище состояния MCE. Она сохраняет произвольные данные ключ-значение, которые можно читать и записывать из узлов CommandGraph, скриптов и файлов сохранения.

Типы переменных

ТипОписаниеПример
BooleanФлаги истина/ложь"defeatedBrock" = true
IntegerЧисловые счётчики"fishCaught" = 42
StringТекстовые значения"rivalName" = "Gary"

Использование в CommandGraph

  • Узел SetVariable: Установить значение переменной.
  • Узел BranchOnVariable: Проверить переменную и разветвить.
  • Узел AddToVariable: Увеличить числовую переменную.

Использование в коде

// Чтение переменной
bool hasBadge = GameVariables.GetBool("defeatedBrock");
int fishCount = GameVariables.GetInt("fishCaught");

// Запись переменной
GameVariables.SetBool("defeatedBrock", true);
GameVariables.SetInt("fishCaught", fishCount + 1);

Пользовательские данные сохранения

Расширение SavableObject (уровень Source)

С исходным кодом создайте новый SavableObject:

public class CustomSaveData : SavableObject
{
[SerializeField] private int customCounter;
[SerializeField] private List<string> unlockedFeatures;

public override void OnSave()
{
// Gather current state before save
customCounter = MySystem.Counter;
unlockedFeatures = MySystem.GetUnlockedFeatures();
}

public override void OnLoad()
{
// Apply loaded state
MySystem.Counter = customCounter;
MySystem.SetUnlockedFeatures(unlockedFeatures);
}
}

Загрузка пользовательских данных (все уровни)

Без доступа к исходному коду используйте события сохранения для сохранения ваших данных параллельно с сохранениями MCE:

public class MySaveExtension : MonoBehaviour
{
[Inject] private ISaveSystem saveSystem;

[Serializable]
private class MyData
{
public int highScore;
public List<string> achievements;
}

private MyData data = new MyData();

private void OnEnable()
{
saveSystem.OnSaved += OnSaved;
saveSystem.OnLoaded += OnLoaded;
}

private void OnSaved(int slot)
{
string json = JsonUtility.ToJson(data);
File.WriteAllText(GetPath(slot), json);
}

private void OnLoaded(int slot)
{
string path = GetPath(slot);
if (File.Exists(path))
{
data = JsonUtility.FromJson<MyData>(File.ReadAllText(path));
}
}

private string GetPath(int slot)
{
return Path.Combine(Application.persistentDataPath, $"custom_{slot}.json");
}
}

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

MCE поддерживает автосохранение по настраиваемым триггерам:

  • При переходе между картами: Сохранение при входе в новую сцену.
  • При отдыхе: Сохранение при лечении в точке отдыха.
  • По временному интервалу: Сохранение каждые N минут.

Настройте автосохранение в ScriptableObject конфигурации игры.

Лучшие практики автосохранения
  • Всегда используйте выделенный слот автосохранения (например, слот 0) отдельно от слотов ручного сохранения.
  • Показывайте индикатор автосохранения при сохранении.
  • Не выполняйте автосохранение во время сражений или катсцен.

Формат файла сохранения

Сохранения MCE -- простой JSON для отладки. Упрощённый пример:

{
"version": 3,
"playerName": "Red",
"playTime": 12345.6,
"currentScene": "PalletTown",
"position": { "x": 8, "y": 12 },
"money": 5000,
"badges": ["BoulderBadge", "CascadeBadge"],
"roster": [
{
"species": 6,
"level": 36,
"nickname": "Charizard",
"currentHP": 120,
"moves": ["Flamethrower", "AirSlash", "DragonClaw", "SwordsDance"],
"ivs": [28, 31, 24, 30, 26, 31],
"evs": [0, 252, 0, 4, 0, 252]
}
],
"gameVariables": {
"defeatedBrock": true,
"defeatedMisty": true,
"fishCaught": 12
}
}
Не редактируйте файлы сохранений

Хотя файлы сохранений -- читаемый JSON, ручное редактирование может вызвать проблемы при введении недопустимых данных (например, приём, не существующий в базе данных). Используйте внутриигровые инструменты или команды отладки.

Лучшие практики

  1. Тестируйте циклы сохранения/загрузки. Сохраните, выйдите, перезагрузите и проверьте, что всё на месте.
  2. Используйте GameVariables для флагов событий. Они сохраняются автоматически и интегрируются с CommandGraph.
  3. Планируйте миграцию сохранений. При добавлении новых данных в обновлении добавьте шаг миграции.
  4. Предоставьте несколько слотов сохранения. Игроки ожидают как минимум 3 слота ручного сохранения.
  5. Обрабатывайте ошибки загрузки корректно. При повреждении файла сохранения показывайте понятную ошибку, а не вылет.