SaveLoad
Namespace: Vortex.Sdk.UIs.SaveLoad
Assembly: ru.vortex.sdk.game.uis.saveload
Активация
Пакет подключается через SdkSettingsSystem:
- Тоггл:
saveLoadWrapperSdkв инспекторе ассетаSdkSettings - Define-символ:
USING_VORTEX_SAVE_LOAD_WRAPPER - Меню:
Tools → Vortex → Configs → SDK Settings
При выключенном тоггле define снимается из PlayerSettings, и пакет не компилируется (asmdef содержит defineConstraints: ["USING_VORTEX_SAVE_LOAD_WRAPPER"]). Канон активации — Vortex/Sdk/SdkSettingsSystem/README.ru.md.
Назначение
UI-обёртка над SaveController (Core SaveSystem): представления списка сейвов, отдельного слота, всплывашка завершения сохранения, а также хэндлеры кнопок Save/Load/Remove. Дополнительно — захват скриншота сцены через многослойный композитинг камер и канвасов и упаковка превью в имя сейва base64-блобом.
Возможности:
- Список сейвов (
SaveListView) с пулом слотов и асинхронным восстановлением превью - Слот-карточка (
SaveView) с подсветкой фокуса, шаблоном имениauto_N/manual_Nи локализованным текстом - Кнопки Save/Load/Remove как
MonoBehaviour-хэндлеры наUIComponent - Многослойный захват экрана (
CameraCaptureHandler) с src_over блендингом черезHidden/AlphaBlit - Кодирование превью в имя сейва (
SavePreviewController, разделитель||, формат JPEG Medium) - Префиксы имени сейва:
auto,manual(SavingSystemConstants)
Вне ответственности:
- Хранение и сериализация сейвов (Core
SaveController) - Структура самого
SaveSummary(CoreSaveSystem) - Сама модель UI-кнопок и пула (
Vortex.Unity.UI.UIComponents,PoolSystem)
Зависимости
Core
Vortex.Core.SaveSystem.Bus—SaveController(Save,Load,Remove,GetIndex,OnSaveComplete,OnLoadComplete,OnRemove,GetNumberLastSave)Vortex.Core.SaveSystem.Abstraction—SaveSummaryVortex.Core.System.Abstractions—IDataStorageVortex.Core.Extensions.LogicExtensions—Base64ToTexture,TextureToBase64,TextureEncodingRulesVortex.Core.Extensions.ReactiveValues—StringDataVortex.Core.Extensions.DefaultEnums—SwitcherStateVortex.Core.LocalizationSystem—Translate
Unity
Vortex.Unity.UI.UIComponents—UIComponent(SetAction,SetText,SetSwitcher)Vortex.Unity.UI.PoolSystem—PoolVortex.Unity.UI.Misc—DataStorageVortex.Unity.UI.TweenerSystem—TweenerHubVortex.Unity.AppSystem.System.TimeSystem—TimeController.Call/RemoveCallVortex.Unity.LocalizationSystem—[LocalizationKey]Vortex.Unity.EditorTools.Attributes—[ClassFilter],[AutoLink]
Sdk
Vortex.Sdk.SdkSettingsSystem— partialSdkSettings(тоггл активации)
External
- UniTask —
UniTask,UniTask.WaitForEndOfFrame,Forget - Sirenix Odin Inspector —
[Button] - Шейдер
Hidden/AlphaBlit(Always Included Shaders)
Архитектура
SaveLoad/
├── SavePreviewController.cs ← упаковка/распаковка превью в имя сейва (base64)
├── SavingSystemConstants.cs ← префиксы AutoName/ManualName
├── AlphaBlit.shader ← Hidden/AlphaBlit (src_over)
├── DefineSettings/
│ ├── SdkSettings.SaveLoad.cs ← partial-кусок SdkSettings (тоггл)
│ └── sdk.settings.system.ext.asmref
├── Handlers/
│ ├── CameraCaptureHandler.cs ← многослойный захват экрана
│ ├── LoadGameHandler.cs ← кнопка загрузки
│ ├── RemoveSaveGameHandler.cs ← кнопка удаления
│ └── SaveGameHandler.cs ← кнопка сохранения + захват превью
├── Models/
│ └── SaveSlotData.cs ← враппер SaveSummary + lazy Texture2D, IDisposable
├── Views/
│ ├── SaveCompleteView.cs ← всплывашка о новом сейве
│ ├── SaveListView.cs ← список сейвов на Pool
│ └── SaveView.cs ← карточка одного слота
└── ru.vortex.sdk.game.uis.saveload.asmdef
Компоненты
| Класс | Тип | Назначение |
|---|---|---|
SavePreviewController |
static class |
GetPreview(SaveSummary), GetSavePureName(this SaveSummary), GetSaveNameWithPreview(Texture2D, string). Разделитель ` |
SavingSystemConstants |
static class |
Константы префиксов: AutoName = "auto", ManualName = "manual" |
SaveSlotData |
class : IDisposable |
Враппер SaveSummary для IDataStorage. Guid, Summary, lazy Preview (Texture2D); Dispose уничтожает текстуру |
CameraCaptureHandler |
MonoBehaviour |
Один слой захвата (Camera или Canvas). Самореестр Handlers, priority (0–10), Render(RT), статический Capture() |
SaveGameHandler |
MonoBehaviour |
Привязка кнопки UIComponent к Save(). Ждёт WaitForEndOfFrame, делает CameraCaptureHandler.Capture(), формирует имя manual_N с превью, вызывает SaveController.Save(name) |
LoadGameHandler |
MonoBehaviour |
Берёт SaveSlotData из IDataStorage, вызывает SaveController.Load(guid) |
RemoveSaveGameHandler |
MonoBehaviour |
Подписан на IDataStorage.OnUpdateLink, вызывает SaveController.Remove(guid) |
SaveListView |
MonoBehaviour |
Заполняет Pool слотами в порядке убывания UnixTimestamp. Хранит StringData _focused и кладёт текущий SaveSlotData в DataStorage. Перезаполнение по OnSaveComplete/OnLoadComplete/OnRemove. Disposable через TimeController.Call |
SaveView |
MonoBehaviour |
Карточка слота. Подписан на _focused.OnUpdateData, переключает SwitcherState.On/Off. Парсит имя auto_N / manual_N и форматирует через LocalizationKey |
SaveCompleteView |
MonoBehaviour |
Всплывашка по SaveController.OnSaveComplete. Прогоняет массив TweenerHub (Back→Forward), выводит имя последнего сейва и Date |
SdkSettings (partial) |
— | Поле saveLoadWrapperSdk с [ToggleButton] и [DefineSymbol("USING_VORTEX_SAVE_LOAD_WRAPPER")] |
Контракт
Вход
- Слой захвата:
CameraCaptureHandlerс заданнымиcameraилиcanvasиpriority - UI-кнопки сохранения/загрузки/удаления:
UIComponentчерезSetAction - Список сейвов берётся из
SaveController.GetIndex()(IDictionary<string, SaveSummary>) - Передача выбранного слота между View и хэндлерами — через
IDataStorage(DataStorage-MonoBehaviour) с модельюSaveSlotData
Выход
- Сохранение:
SaveController.Save(name)где имя содержит base64-превью после|| - Загрузка/удаление:
SaveController.Load(guid)/SaveController.Remove(guid) - Превью слота:
RawImage.texture = SaveSlotData.Preview
Гарантии
SaveListViewсортирует индекс по убываниюUnixTimestamp, первый ставится в фокусSaveSlotData.Previewсоздаётся лениво,DisposeуничтожаетTexture2DSaveListView.Disposeотписывается от событий, отменяет CTS, чистит пул и слотыCameraCaptureHandler.Capture()сортирует активные хэндлеры поpriority(меньше — ниже), композитит через src_overSaveGameHandlerждётWaitForEndOfFrameперед захватом — пайплайн консистентен при вызове из EventSystemRemoveSaveGameHandlerпере-инициализируется наIDataStorage.OnUpdateLink
Ограничения
- Шейдер
Hidden/AlphaBlitобязан быть в Always Included Shaders (Project Settings → Graphics) - Один
CameraCaptureHandlerнесёт ровно одну сущность:cameraилиcanvas. При обоих null —LogWarning - Для
Canvas.ScreenSpaceCameraбезworldCamera—LogWarning, слой пропускается - Имя сейва не должно содержать подстроки
||(она вырезается черезReplace) - Возвращённую
Capture()текстуру обязан уничтожить вызывающий код (Destroy)
Использование
Сцена со списком сейвов
- На корне UI разместить
SaveListViewсо ссылками наPool(префаб слота соSaveView) иDataStorageдля текущего фокуса. - На префабе слота:
SaveViewс[AutoLink]на источникIDataStorage, ссылками наslotButton(UIComponent),slotName,timestamp,RawImage slotImageи ключи локализацииautoSavePattern/manualSavePattern. - Кнопки
Load/Remove:LoadGameHandler/RemoveSaveGameHandlerс тем жеDataStorage.
Кнопка «Сохранить»
// На GameObject с UIComponent (кнопкой) добавить SaveGameHandler.
// Имя сейва будет вида: manual_N || <base64 jpeg>
Захват экрана
// На каждой камере / канвасе разместить CameraCaptureHandler,
// проставить priority (меньше — ниже).
// В Always Included Shaders добавить Hidden/AlphaBlit.
await UniTask.WaitForEndOfFrame(this);
var screenshot = CameraCaptureHandler.Capture();
// ... использовать ...
UnityEngine.Object.Destroy(screenshot);
Извлечение превью из существующего сейва
SaveSummary summary = ...;
Texture2D preview = SavePreviewController.GetPreview(summary); // либо null
string pureName = summary.GetSavePureName();
Граничные случаи
| Ситуация | Поведение |
|---|---|
| Пустой индекс сейвов | SaveListView чистит пул, фокус не выставляется |
SaveSummary.Name без ` |
|
Имя слота не подходит под шаблон auto_N / manual_N |
Выводится сырое saveName без локализации |
IDataStorage без SaveSlotData |
LoadGameHandler пишет LogError и выходит; RemoveSaveGameHandler отвязывает кнопку |
Capture() без активных хэндлеров |
LogWarning, возврат null |
| Изменение размера экрана между кадрами | CameraCaptureHandler.Render пересоздаёт RenderTexture |
Canvas.ScreenSpaceOverlay |
Захват через временную ортографическую камеру с cullingMask = 1 << layer, режим/worldCamera/planeDistance восстанавливаются |
Disable SaveListView |
Dispose запланирован через TimeController.Call, отписки выполняются вне OnDisable-кадра |