Если коротко:
Level streaming делит VR-мир на части и загружает следующую область до того, как пользователь войдёт в неё. Асинхронное чтение сокращает паузу, но активация объектов, загрузка ресурсов в GPU и освобождение памяти всё ещё могут растянуть кадр, поэтому переход проверяют профилировщиком на целевой гарнитуре.
Level streaming VR загружает и выгружает части мира во время работы приложения. Вместо смены всей карты с неподвижным экраном проект сохраняет небольшую постоянную сцену, заранее читает следующую область и активирует её у выбранной границы. Метод сокращает длинные паузы, но сам по себе не делает переход бесшовным.
Что именно нужно разделить
Постоянный слой обычно содержит XR rig, камеру, ввод и менеджеры состояния. Геометрию, аудио и логику зон помещают в загружаемые сцены или ячейки. Общие сервисы остаются в постоянном слое, локальные зависимости загружаются вместе.
Крупная область создаёт высокий пик памяти и долго готовится, а множество мелких блоков увеличивает число операций и зависимостей. Удобные границы проходят через коридор, лифт, шлюз или точку телепорта: такой переход даёт системе время подготовить контент.
Level streaming не следует путать с Pixel Streaming: второй метод передаёт видеопоток с удалённого компьютера, а не управляет локальными сценами.
Async loading не равен готовой сцене
Асинхронная операция переносит часть чтения из блокирующего вызова. Затем движок создаёт объекты, связывает компоненты, загружает ресурсы, регистрирует физику и выполняет пользовательский код. Эти этапы могут попасть на главный поток и вызвать frame-time spike.
Разделяйте состояния requested, loaded и activated: загрузку запускают заранее, видимость и логику включают после проверки готовности. Тяжёлую активацию второстепенных объектов распределяют по кадрам. Шейдеры, пулы и эффекты можно прогреть до перехода, учитывая стоимость прогрева.
При предзагрузке в памяти одновременно находятся текущая и следующая области плюс постоянный слой. Ранняя выгрузка показывает пустоту при задержке накопителя, поздняя повышает риск нехватки RAM.
Граница стриминга и запас времени
Граница должна учитывать расстояние и скорость. Для телепорта полезно создать streaming source у точки назначения, дождаться готовности и лишь затем перенести игрока.
Используйте внешнюю границу для preloading, внутреннюю для показа и оставляйте между ними буфер времени. Гистерезис не даёт соседней области постоянно загружаться и выгружаться, когда пользователь стоит на пороге.
Для медленного накопителя предусмотрите закрытую дверь, переходный объём или остановку с индикацией. Это безопаснее пустого пространства и пропавшей коллизии. Полностью скрыть загрузку на каждом устройстве нельзя.
Реализация в Unity
SceneManager.LoadSceneAsync загружает сцену асинхронно. Режим LoadSceneMode.Additive добавляет её к уже активной, а AsyncOperation сообщает о ходе операции.
Свойство allowSceneActivation отделяет подготовку от активации, но у него есть важное ограничение. Пока значение false, операция останавливается около отметки 0,9, isDone остаётся ложным, а очередь последующих AsyncOperation может ждать. Не запускайте зависящую выгрузку в заблокированной очереди и не воспринимайте progress как точный процент всей пользовательской подготовки.
Addressables даёт дескрипторы и управление зависимостями. Сохраните handle сцены для UnloadSceneAsync: при activateOnLoad: false её можно подготовить заранее и активировать через SceneInstance.
В Unity полезно вынести переход в явный контроллер:
- запросить additive-сцену до границы;
- дождаться готовности нужных зависимостей;
- активировать зону и только затем переключить gameplay state;
- проверить ссылки и коллизии;
- выгрузить старую сцену, когда игрок уже не может вернуться в неё мгновенно.
Не совмещайте тяжёлую очистку ресурсов с показом новой области: сначала измерьте стоимость выгрузки на целевой сборке.
Реализация в Unreal Engine
Unreal Level Streaming загружает, выгружает и скрывает части карты. Для проектов Unreal Engine 5 Epic рекомендует World Partition: постоянный Level делится на grid cells вокруг streaming sources.
World Partition различает состояние Loaded и Activated. Это позволяет подготовить ячейку до показа. Streaming Source Component размещают на игроке или у будущей точки телепорта. Функция Is Streaming Completed помогает проверить, закончена ли работа для затронутых ячеек, перед переходом.
Размер cells и loading range выбирают по скорости, дальности обзора и весу ресурсов. Короткий диапазон показывает подгрузку, чрезмерный держит лишние данные. Runtime Data Layers переключают варианты окружения, но слишком широкое применение слоёв ухудшает streaming performance.
Для специальных сценариев остаются sublevels, Level Streaming Volumes, Blueprint и C++. World Partition снижает объём ручного управления, но не отменяет проверку зависимостей и стоимости активации Actor.
Бюджет памяти
Составьте таблицу для каждой зоны: текстуры, меши, аудио, анимации, навигация и служебные данные. Укажите обычное потребление и переходный пик, когда соседние области загружены одновременно. Бюджет должен оставлять место runtime, системным процессам и временным буферам.
На автономной гарнитуре доступная приложению память меньше физического объёма устройства, потому что часть резервирует система. Meta рекомендует измерять PSS и проверять low-memory события на устройстве. Поэтому показатели Editor или PC не заменяют профиль целевой сборки.
Общие ресурсы не должны дублироваться в каждом блоке. Отслеживайте владельца зависимости и освобождайте её только после ухода всех потребителей.
Где возникают скачки времени кадра
Профилируйте отдельно запрос, чтение, активацию и выгрузку. CPU-всплеск может появиться в Awake, Start, BeginPlay, регистрации компонентов, создании коллизии или сборке мусора. На стороне GPU и драйвера задержку способны вызвать загрузка текстур, подготовка pipeline state и первый показ материала.
Добавьте маркеры вокруг каждой стадии и запишите несколько одинаковых проходов. В Unity используйте Profiler и Memory Profiler; порядок измерений описан в статье о профилировании VR-проекта в Unity. В Unreal сопоставьте переход с Game, Render и RHI threads через Unreal Insights.
Тестируйте на медленном поддерживаемом устройстве и в собранном приложении. Первый проход может включать холодное чтение и компиляцию, а повторный пользоваться кэшем. Оба сценария важны: новый пользователь видит первый, а возвращающийся чаще сталкивается с прогретым состоянием.
Рабочий порядок настройки
Сначала зафиксируйте допустимый memory budget и время кадра для выбранной частоты дисплея. Затем соберите один переход между двумя репрезентативными зонами. Измерьте его без маскировки и найдите самый дорогой этап.
После этого перенесите запрос дальше от границы, разделите загрузку и активацию, сократите самые тяжёлые ресурсы либо подготовьте их заранее. Меняйте один фактор за раз и повторяйте маршрут. Общие рекомендации по поиску ограничения Unreal-проекта собраны в материале об оптимизации VR в Unreal Engine.
Готовый переход должен выдерживать быстрый вход, возврат через границу и нехватку памяти без потери коллизии или состояния задания. Level streaming уменьшает заметную паузу, когда архитектура, расстояния и бюджет согласованы. Он не гарантирует бесшовность без измерений на реальном устройстве.