Приемы сохраняемости
Сохраняемость доступна при ее поддержке на базовой платформе. В настоящее время она ограничена семейством устройств HoloLens с использованием встроенной поддержки виртуальной реальности Unity (устаревшая смешанная реальность).
Базовая сохраняемость
Базовая сохраняемость для World Locking Tools включена по умолчанию. Она реализуется в следующем.
Нам важны флажки Auto Load (Автоматическая загрузка) и Auto Save (Автоматическое сохранение), которые здесь установлены. Вы можете заметить, что они недоступны. Это связано с тем, что выбран вариант Use Defaults (Использовать настройки по умолчанию). Если отключить использование настроек по умолчанию, вы можете выбрать любое сочетание параметров автоматизации.
Дополнительные сведения см. в статьях об этих параметрах и об их изменении из скрипта.
Автоматическое сохранение
Параметр AutoSave указывает WLT создавать частые и регулярные сохранения состояний при выполнении приложения. Работу приложения можно завершить в любой момент с минимальной потерей состояния.
AutoLoad
Параметр AutoLoad указывает WLT загрузить ранее сохраненное состояние при запуске приложения. Это позволяет приложению возобновить работу в новом сеансе с момента завершения в прошлом сеансе (с WLT).
Полная сохраняемость
Если включены оба параметра AutoSave и AutoLoad, WLT работает без прерывания во всех сеансах. Хотя позиция и поворот глобального пространства задаются произвольно при первом запуске (так как сохраненное ранее состояние отсутствует, в качестве начальной точки используется положение головы при запуске), в последующих запусках будет использоваться одна система координат.
Это приводит к интересному поведению, когда приложение запускает новый сеанс в пространстве, отсоединенном от пространства предыдущего сеанса. Дополнительные сведения см. в разделе Сохраняемость по расположению ниже.
Примечание
Параметры AutoSave и AutoLoad также применяются к глобальным пространственным маркерам (SpacePin). Подробные сведения см. ниже.
Управление сохраняемостью со стороны приложения
Полная сохраняемость по умолчанию подходит для широкого спектра приложений.
Но некоторым приложениям может потребоваться дополнительный контроль над этим процессом.
Может показаться странным, что включение автоматической сохраняемости WLT привязано к двум свойствам: AutoSave и AutoLoad. Изучив случаи, где оба из них используются независимо, вы сможете лучше понять, как работает система сохраняемости.
AutoSave без AutoLoad
В этой конфигурации WLT периодически сохраняет свое состояние, но при запуске оно не будет загружаться автоматически.
Система будет запускаться в новом состоянии, как при первом запуске на этом устройстве. Состояние предыдущего сеанса будет восстановлено только после явного запроса к Load().
Это позволяет приложению определять, следует ли выполнять состояние предыдущего сеанса, и даже при необходимости изменять восстанавливаемые данные.
Общее сохраненное состояние WLT находится в файле LocalState/frozenWorldState.hkfw. После создания с помощью WLT этот файл можно скопировать в другое расположение и восстановить, если того потребует приложение.
Для данных выравнивания (SpacePin) используется файл сохранения LocalState/Persistence/Alignment.fwb. Но приложение может переопределить его с помощью SaveFileName диспетчера выравнивания.
Решение по загрузке состояния предыдущего сеанса с этой конфигурацией нужно принять при запуске. После запуска сохраненное состояние предыдущего сеанса будет перезаписано этим состоянием сеанса. Сведения о более гибкой настройке см. в разделе Сохранение и загрузка вручную ниже.
Сохранение вручную с AutoLoad
В этой конфигурации WLT будет при запуске загружать любое доступное состояние из предыдущего сеанса, но не будет автоматически сохранять состояние. Это позволяет приложению принимать решение относительно целесообразности и времени сохранения состояния с помощью вызова к Save().
AutoLoad только указывает WLT загрузить любое доступное состояние при запуске. Приложение может свободно восстановить любое сохраненное состояние в любое время с помощью явного вызова к Load().
Сохранение и загрузка вручную
Приложение может решить сохранить полный контроль над процессом сохранения и загрузки.
В таком случае состояние будет сохраняться только при явном вызове от приложения к Save() и загружаться только при явном вызове к Load().
Состояние, загружаемое вызовом к Load(), могло быть сохранено ранее в этом сеансе или в предыдущем сеансе.
Отключение сохраняемости
Как описано выше, сохраняемость всегда доступна приложению из скрипта. Автоматическую сохраняемость можно включить и отключить из скрипта или через WorldLockingContext в Inspector. Если автоматическая сохраняемость отключена, WLT не будет пытаться сохранить или загрузить состояние без явного запроса от приложения.
Конечно же, так как директива AutoLoad влияет только на загрузку при запуске, изменение значения из скрипта после запуска не будет иметь эффекта.
Предупреждение для разработчиков
Как указано выше, расположение файлов сохранения для глобальной среды WLT и выравнивания глобальны по отношению к приложению. В частности, узлы выравнивания, также называемые пространственными маркерами (SpacePin), сохраняются по имени (см. ниже). Если приложение сохраняет состояние с набором пространственных маркеров из одной сцены, а затем загружает состояние с набором пространственных маркеров из другой и оба набора имеют одни имена, поведение будет непредвиденным.
Существует несколько способов решить эту проблему. При возможности просто избегайте повторного использования имен пространственных маркеров в проекте. Если после повторного развертывания вы сталкиваетесь с непредвиденным поведением скольжения сцены, попробуйте удалить сохраненное состояние WLT. Аналогично, при кардинальном изменении приложения, чтобы избежать проблем, можно удалить файлы сохранений WLT с устройства или просто удалить приложение перед установкой новой версии.
Сохраняемость по расположению
Сценарий
Существует интересный класс приложений, которые выполняются в нескольких физических расположениях. Например, приложение может быть запущено в комнате A, после чего устройство закрывается, перемещается, и приложение перезапускается в комнате Б. Комната Б может располагаться дальше по коридору или на другом континенте. Приложение и устройство никак не могут определить это.
Для простоты предположим, что приложение настроено для сохраняемости WLT вручную.
Пошаговый разбор
Рассмотрим такие несвязанные комнаты A и Б.
Приложение запускается в комнате A. После создания непрерывного замороженного координатного пространства в комнате, вся комната сопоставляется с фрагментом 1. В комнате размещается сохраняемый объект X голограммы. Затем приложение сохраняет состояние и завершает работу.
Устройство отключается, перемещается в комнату Б и снова запускается.
Устройство распознает, что находится не в комнате А, поэтому WLT назначает новый идентификатор фрагмента его содержимому, например 29. Почему 29? Потому что это не 1. Идентификаторы фрагментов могут иметь произвольное значение, но не должны быть FragmentId.Invalid, FragmentId.Unknown или совпадать с идентификатором другого известного фрагмента.
Теперь у вас есть два фрагмента без возможности объединить их (так как отсутствуют сведения об их относительном расположении).
Интересующийся разработчик приложений может спросить: "Я размещаю сохраняемый объект X в комнате A. Что происходит при загрузке объекта X при запуске приложения в комнате Б?".
Ответ — поведение в таком случае определяет разработчик приложения. Текущий идентификатор фрагмента при размещении объекта X в комнате A доступен и может быть сохранен. Приложение затем может решить при запуске, следует ли отображать объект X, с учетом соответствия текущего фрагмента созданному ранее.
Здесь именно разработчик решает (и реализует), что объект X будет загружаться только в том случае, если текущий идентификатор фрагмента имеет значение 1, и объект Y из комнаты Б будет загружаться только в том случае, если текущий идентификатор фрагмента имеет значение 29.
Сохраняемость идентификатора фрагмента, связанного с пространством, сохраняется в процессе реализации сохраняемости World Locking Tools. Но сохраняемость идентификатора фрагмента, связанного с объектом, а также действия на ее основе реализуются самим приложением.
Вместе со связанным идентификатором фрагмента для объекта можно сохранить его положение в глобальном пространстве. Затем, если идентификаторы фрагментов совпадают, после загрузки объекта его положение можно восстановить, вернув его в позицию в физическом мире во время последнего сеанса. Благодаря сохраняемости World Locking Tools положение остается фиксированным в сеансах относительно окружающих признаков физического мира.
Сохраняемость пространственных маркеров (SpacePin)
Классы SpacePin можно рассматривать как оболочки для AlignmentAnchor на стороне приложения. В то время как пространственные маркеры (и производные классы) являются компонентами Unity, привязки AlignmentAnchor являются полностью абстрактными (для них отсутствует соответствующий класс или тип). Поэтому в этой статье пространственные маркеры (SpacePin) и AlignmentAnchor будут использоваться как синонимы, но предпочтение будет отдаваться первым.
Но то, что AlignmentManager может сохранять пространственные маркеры, не имея представления о них, может создавать путаницу. Это связано с тем, что AlignmentManager управляет абстрактной привязкой AlignmentAnchor, которая воплощает сущность SpacePin и из которой можно воссоздать SpacePin.
Для сохраняемости маркеров SpacePin доступно больше элементов управления на уровне приложения, чем в системе общей сохраняемости WLT, так как маркеры SpacePins изначально более подвержены влиянию входных данных приложения, чем остальные средства World Locking Tools.
Важно помнить, что маркеры SpacePin (и привязки AlignmentAnchor) сохраняются по имени. Это немного более строгое требование, чем общее, касающееся того, что два активных пространственных маркера в одном IAlignmentManager должны иметь разные имена. Если маркеры SpacePin сохраняются, два SpacePin в одной базе данных не могут иметь одно имя независимо от того, активны они или нет.
Базы данных диспетчера выравнивания
Каждый IAlignmentManager имеет базу данных маркеров SpacePin с их именами, как можно понять из его реализации RestoreAlignmentAnchor(string uniqueName, Pose virtualPose).
Глобальная база данных выравнивания
Существует один глобальный IAlignmentManager, принадлежащий WorldLockingManager.GetInstance(). Как сказано ранее, его расположение файлов сохранения по умолчанию определяется свойством SaveFileName. Обратите внимание, что SaveFileName — это свойство класса AlignmentManager, а не IAlignmentManager интерфейса. Реализация IAlignmentManager может реализовывать сохраняемость, не имея представления о файлах или именах файлов. SaveFileName — это артефакт способа, которым AlignmentManager реализует сохраняемость, поэтому он ограничен AlignmentManager.
Локальные базы данных выравнивания
Может существовать неограниченное число диспетчеров выравнивания подпространств, по одному на каждое дерево AlignSubtree, при этом они отображаются как AlignSubtree.alignmentManager поля. Кроме того, приложение может создать собственные экземпляры AlignmentManager или даже собственные производные классы IAlignmentManager.
Каждый AlignmentManager компонента AlignSubtree имеет собственное расположение файла сохранения, который по умолчанию имеет имя GameObject с расширением FWB. Например, если компонент AlignSubtree находится в GameObject с именем "MyRoot", файл сохранения будет иметь имя "MyRoot.fwb". Для его размещения во вложенной папке можно использовать косую черту ("/"). Вероятно, не очень оптимально, если два компонента AlignSubtree используют одно расположение файла сохранения.
Но действительно
Мы настоятельно рекомендуем для простоты и повышенной надежности давать SpacePin/AlignmentAnchor глобально уникальные имена, а не ограничиваться выполнением требований к локальной уникальности. Но выбор остается за вами.