Сохранения и мультиплеер

Средний Обновлено 9 июл. 2026 г.

Содержание

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

Автосейв / автозагрузка

Квестовый компонент каждого игрока загружает сохранённый прогресс при старте и сохраняет после изменений (принятие квеста, засчитывание цели, переход на следующий шаг, флаги, завершение). Сохранения объединяются: серия изменений в одном кадре стоит одну запись. Финальное сохранение выполняется при выходе, смене карты и логауте, а автозагрузка автоматически восстанавливает прогресс при переходах между уровнями.

Политика настраивается в Project Settings → Plugins → Questwright Quests (подробности в справочнике настроек):

Project Settings с опциями сохранения Questwright Quests

Автоматическое сохранение включается для компонентов, принадлежащих PlayerState (штатная схема развёртывания); компоненты на обычных акторах остаются полностью в ручном режиме SaveProgress() / LoadProgress().

Что сохраняется

Только записи прогресса каждого игрока: состояние, текущий шаг, счётчики целей, флаги, временные метки. Контент квеста живёт в скомпилированных ассетах, а активная сцена диалога относится к презентации и никогда не сохраняется. Сохранённый прогресс переживает обновления контента: запись, чей сохранённый шаг больше не существует, откатывается к первому шагу квеста (с предупреждением в логе), а записи квестов, неизвестных текущей сборке, остаются нетронутыми.

Три уровня

РазвёртываниеЧто нужно сделать
Одиночная играНичего. Один слот USaveGame (Questwright_Quests).
Кооператив на listen-сервереНичего. Каждый игрок получает отдельный слот по его онлайн-идентификатору (Questwright_Quests_<id>).
Выделенный сервер / свой бэкендРеализуйте один C++-интерфейс (ниже).

Бэкенд подключается через интерфейс IQuestwrightSaveProvider:

class UMyDbSaveProvider : public UObject, public IQuestwrightSaveProvider
{
    // PlayerKey is the player's UniqueNetId string; use it as your database key.
    virtual bool SaveQuestRecords(const FString& PlayerKey,
                                  const TArray<FQuestwrightQuestRecord>& Records) override;
    virtual bool LoadQuestRecords(const FString& PlayerKey,
                                  TArray<FQuestwrightQuestRecord>& Out) override;
    // Optional non-blocking variant for REST/database fetches:
    // virtual void LoadQuestRecordsAsync(...) override;
};

Зарегистрируйте его как SaveProviderClass на странице настроек или внедрите экземпляр вызовом SetSaveProvider() при логине. Всё выполняется на стороне сервера; клиенты получают загруженный прогресс через обычную репликацию.

Ручное управление

Для игры с точками сохранения: установите AutoSavePolicy в Manual и вызывайте SaveProgress() / LoadProgress() сами. OnProgressLoaded срабатывает после применения сохранённого прогресса.

Модель мультиплеера

Questwright работает в standalone, на listen-сервере и на выделенном сервере со строгим серверно-авторитетным разделением:

  • Прогресс индивидуален для каждого игрока: он хранится в компоненте на PlayerState и реплицируется только владельцу, поэтому игроки не видят внутренности чужого журнала.
  • Все изменения серверно-авторитетны. Клиентские вызовы (принятие квеста из UI, сообщение о событии) автоматически идут через Server RPC, так что API выглядит одинаково с обеих сторон.
  • Засчитывание убийства достаётся игроку-инициатору, если ваш код смерти передаёт убийцу; иначе засчитывается всем игрокам (фолбэк для общего мира).
  • Презентация никогда не выполняется на сервере. Камеры, лица, UI диалога и взгляд работают только на клиенте-владельце; выделенный сервер не выполняет ничего из этого.
  • Компоненты создаются автоматически при логине: присоединившиеся позже игроки получают свои без дополнительных действий.