Спроектируйте архитектуру сохранения и загрузки для игры на Unreal Engine с постоянным состоянием мира — прогресс игрока, инвентарь и динамически заспавненные акторы, которые должны вернуться при загрузке. Множество независимых систем владеют каждая своей частью сохраняемого состояния, поэтому каждая должна вносить вклад в сохранение и восстанавливаться из него без монолитной процедуры, знающей все их внутренности. Формат должен переживать сборки и платформы (он не должен ломаться при изменении классов между версиями), поэтому нельзя просто сбрасывать память живых объектов и сырые указатели — нужно стабильное представление, пересоздающее живые объекты при загрузке. Сохранение должно быть явным и надёжно писаться на диск, а не считаться сохранённым в памяти. В мультиплеере состояние сохраняет только авторитетная сторона. Опишите, что вы храните, а что пересоздаёте, как подключаются независимые системы, как держите сохранение устойчивым между версиями, и компромиссы вашего подхода.
Используйте подкласс USaveGame как контейнер данных, сериализуемый через UGameplayStatics::SaveGameToSlot. Каждая сохраняемая система пишет состояние в объект при сохранении и восстанавливает при загрузке; не сериализуйте живые Actor'ы напрямую — храните id и пересоздавайте.
- ✗Пытаться сериализовать указатели на живые Actor'ы вместо хранения id и пересоздания при загрузке
- ✗Использовать сырой
memcpyструктур, что ломается между сборками и платформами - ✗Считать, что состояние в памяти сохранится без явной записи в слот сохранения
- →Как бы вы сохраняли и восстанавливали динамически заспавненные Actor'ы и их трансформы?
- →Как бы вы обработали сохранение, записанное более старой версией игры?
Ключевые классы
UMySaveGame— подклассUSaveGame: плоский контейнерUPROPERTY-полей и структур. Без логики.USaveGameSubsystem—UGameInstanceSubsystem: оркестрирует сохранение/загрузку, опрашивает системы.ISavable— интерфейс на системах/Actor'ах:WriteSaveData,ReadSaveData.FActorSaveData—USTRUCT: класс, трансформ, id и сериализованные свойства одного актора.
Модель данных
USaveGame хранит данные, а не объекты: вместо указателей на Actor'ы — их id, класс и трансформ. Живые объекты пересоздаются при загрузке. Свойства актора сериализуются через FObjectAndNameAsStringProxyArchive поверх SaveGame-флага у UPROPERTY.
Поток данных
Сохранение:
USaveGameSubsystemсоздаётUMySaveGame.- Каждая
ISavable-система пишет своё состояние; динамические Actor'ы пишутFActorSaveData. SaveGameToSlot(SaveObj, Slot, UserIndex)пишет на диск асинхронно.
Загрузка:
LoadGameFromSlotчитает объект.- Подсистема проверяет поле версии, при необходимости мигрирует.
- Пересоздаёт динамические Actor'ы по
FActorSaveData, затем раздаёт состояние системам.
Соображения по репликации
В мультиплеере сохраняет только сервер — клиенты не имеют авторитетного состояния. После загрузки сервер реплицирует восстановленное состояние клиентам штатными механизмами; отдельной сетевой логики у системы сохранения нет.
Настройка дизайнером
Какие Actor'ы сохраняются — помечается тегом или реализацией ISavable в Blueprint. Поля под сохранение помечаются спецификатором SaveGame у UPROPERTY. Имя слота и автосейв-точки настраиваются данными.
Компромиссы
- Декларативно (флаг
SaveGame) vs. ручная сериализация: флаг меньше кода, но меньше контроля над форматом и версионированием. - Один большой слот vs. модульные слоты: один проще, модульные дешевле для частых автосейвов.