Файловая система
UNIGINE имеет собственный модуль файловой системы, используемый для доступа к файлам и папкам. У него есть несколько особенностей, о которых вы должны знать при загрузке ресурсов или организации структуры вашего проекта на основе UNIGINE.
GUID#
В файловой системе UNIGINE каждый файл имеет GUID (глобальный уникальный идентификатор), который определяет виртуальный путь к этому файлу (а не к файлу на диске). Использование GUID обеспечивает более гибкое управление файлами: вы можете абстрагироваться от имен файлов (которые могут быть одинаковыми в разных папках проекта). Например, вы можете изменить путь к файлу, сохранив тот же идентификатор GUID.
Движок генерирует GUID для всех файлов виртуальной файловой системы .
К файлам файловой системы UNIGINE можно получить доступ, используя как имена, так и идентификаторы GUIDs: вы можете получить GUID для конкретного файла, изменить GUID файла, добавить / удалить файл с определенным именем или GUID из BLOB-объекта или кешированного файла и так далее.
Если UnigineEditor загружен, он автоматически сохраняет сгенерированные идентификаторы GUID в файл data/guids.db. В противном случае вы можете реализовать логику обновления guids.db с помощью кода. В файле guids.db хранится пара: GUID файла и путь к нему относительно папки data.
Поскольку файловая система UNIGINE предоставляет точки монтирования для расширения проекта, в одном проекте может быть несколько файлов guids.db. Каждая точка монтирования может хранить свой собственный guids.db: GUID для внешних файлов не записываются в data/guids.db.
Удаление файла guids.db не вызовет никаких проблем: если файла guids.db нет, движок будет искать среди файлов .meta. Однако guids.db может потребоваться для окончательной сборки проекта, если вы не собираетесь включать файлы .meta.
Параметр Ignore для guids.db#
Иногда в случае совместной разработки недопустимый файл guids.db может быть зафиксирован в репозитории (из-за неправильного слияния или иным образом), что приведет к ошибкам при работе с UnigineEditor . Вы можете заставить движок игнорировать файл guids.db с помощью параметра командной строки запуска -skip_guidsdb . В этом случае движок ищет идентификаторы GUID среди всех файлов .meta внутри папки data и всех смонтированных внешних каталогов и пакетов. UnigineEditor использует этот аргумент по умолчанию, чтобы избежать ошибок, и всегда повторно генерирует файл guids.db, чтобы гарантировать его достоверность.
Обновление файловой системы#
Динамическое сканирование#
Динамическое сканирование позволяет движку формировать виртуальную файловую систему для всех файлов в папке data (включая те, которые находятся внутри точек монтирования ). Динамическое сканирование выполняется при запуске двигателя. Для файлов, физически хранящихся в папке data, отслеживание изменений файлов включено в реальном времени с использованием относительных путей для адресации файлов.
Если изменения в файловой системе вносятся не с помощью UnigineScript, вам может потребоваться вызвать консольную команду filesystem_reload .
Автоматическая перезагрузка ресурсов#
Когда UnigineEditor загружен, он отслеживает изменения, внесенные в файлы во время выполнения: он проверяет время последней модификации таких файлов и обновляет их в памяти. Если UnigineEditor не загружен, измененные файлы перезагружаются после перезагрузки мира .
Известные и неизвестные файлы#
Если вы добавляете новые файлы во время выполнения, движок ничего не будет знать о таких файлах (поскольку виртуальная файловая система была сформирована при запуске). Повторное сканирование файловой системы требует значительных ресурсов, поэтому вы можете добавлять новые файлы в виртуальную файловую систему через API с помощью addVirtualFile() .
Каталог данных#
Все файлы, используемые движком во время выполнения, хранятся в папке data, указанной с помощью параметра запуска -data_path . По умолчанию это папка data, автоматически создаваемая при создании проекта с помощью UNIGINE SDK Browser .
Когда файловая система определяет пути, она пытается связать пути данных с указанным путем и выполнить поиск. В случае абсолютных путей файловая система будет использовать их «как есть» без каких-либо проверок.
Например, если папка проекта имеет следующую структуру:
-
unigine_project
- bin
- data
После запуска приложения путь к данным будет равен unigine_project/data:
bin\main_x64d.exe -data_path "../"
Текущий каталог#
Если указанный -data_path является абсолютным, текущий рабочий каталог может отличаться от каталога с исполняемым двоичным файлом. Однако, когда путь к каталогу данных является относительным, Engine переключает текущий каталог на каталог с исполняемым двоичным файлом.
При доступе к файлу вне каталога data через API, путь к такому файлу должен быть указан относительно текущего каталога. Например:
// cbox.mesh is stored outside the data directory, so the path is specified relative to the current directory
ObjectMeshStaticPtr cbox = ObjectMeshStatic::create("../../data/cbox.mesh");
Корневой файл монтирования#
Строительным блоком виртуальной файловой системы является монтирование : файловая система создается как корневое монтирование и может быть легко расширена с помощью дополнительных точек монтирования . Этот подход позволяет расширить виртуальную файловую систему вашего проекта, добавляя любые внешние папки и пакеты в каталог данных.
При создании проекта файл root_mount.umount создается в папке data. Это файл в формате JSON, представляющий корневую точку монтирования. Он хранит версию UNIGINE SDK, в которой было создано корневое монтирование, и игнорирует фильтры (т.е. указание на папки , которые следует игнорировать):
{
"version": "2.9.0.0"
"ignore_filters": [
".svn",
".git",
".teamcity",
".thumbnails"
]
}
Файл root_mount.umount уже содержит фильтры игнорирования по умолчанию, и при необходимости список может быть расширен с помощью подстановочных знаков.
Путь задается относительно папки data. Если папка, которую следует игнорировать, находится внутри любой другой папки (например, data/folder_1/.svn), относительный путь к этой папке должен быть установлен как фильтр игнорирования (folder_1/.svn).
Если проект не содержит файла root_mount.umount (проект был создан с помощью предыдущей версии UNIGINE SDK или файл был удален), проект будет запущен без игнорирования папок в data.
Файл root_mount.umount можно создать вручную или с помощью кода с использованием API .
Файловые пакеты#
Типы#
UNIGINE поддерживает следующие типы файловых архивов для экономии места или упаковки производственной версии ресурсов:
- UNG (собственный формат UNIGINE для архивов, созданных с помощью инструмента Archiver)
Максимальный размер файла внутри архива UNG ограничен 2 GB.
- ZIP
- Пользовательские пакеты C ++ , созданные с помощью UNIGINE API
Помимо экономии места, архивы также ускоряют загрузку ресурсов, поскольку файлы в архиве читаются линейно.
Архивы UNG и ZIP загружаются автоматически, если они находятся в папке data. Файлы добавляются в виртуальную файловую систему так же, как и неархивированные файлы.
Доступ к контенту#
Архивы полностью прозрачны для Engine. Нет необходимости явно распаковывать архивы, поскольку их содержимое автоматически обрабатывается как незапакованное. К архивным файлам обращаются как к неархивированным. Например, если у вас есть data/project/archive.ung и вы хотите адресовать в нем directory/file.txt, просто укажите следующий путь: project/directory/file.txt.
Внутри архива файлы могут быть организованы любым способом. Однако в корень архива следует помещать только файлы с уникальными именами. В противном случае поиск файлов даст неверные результаты.
Вот пример неверного дерева файлов для архива:
-
my_archive.ung
-
my_folder
- file_2.txt
- file_1.txt
- file_2.txt
-
my_folder
В этом случае проблем с file_1.txt нет, так как его имя уникально. file_2.txt, с другой стороны, вызовет проблемы: он не гарантирует, что будет возвращен некорневой файл.
Правильная структура архива может быть указана следующим образом:
-
my_archive.ung
-
my_folder
- file_2.txt
-
another_folder
- file_2.txt
- file_1.txt
-
my_folder
В этом случае файлы с одинаковыми именами хранятся в разных каталогах, поэтому поиск файлов будет совершенно правильным.
Если существует конфликт имен между заархивированным и неархивированным файлом, возвращается первый соответствующий файл. Поиск производится в следующем порядке:
- Неархивированные файлы
- Файлы в UNG-архивах
- Файлы в in ZIP-архивах
Из UNIGINE API архивы также обрабатываются с помощью функций FileSystem.
Расширение файловой системы#
Виртуальную файловую систему можно легко расширить с помощью функции точки монтирования . Это позволяет вам расширять виртуальную файловую систему вашего проекта, добавляя любые внешние папки и пакеты в каталог данных.
Использование точек монтирования в вашем проекте позволяет вам использовать сохраненный контент:
- В одной папке или репозитории для нескольких проектов.
- В нескольких папках для одного проекта. Вы можете создать столько точек монтирования, сколько требуется для проекта.
Проект на основе UNIGINE имеет единственный каталог data. Здесь хранятся все активы проекта и файлы времени выполнения. Кроме того, папка data может хранить точки монтирования, созданные с помощью Asset Browser (Create Mount Point) или API .
Точка монтирования представлена на диске как файл *.umount: файл в формате JSON, в котором хранится ссылка на внешний каталог или пакет как абсолютный путь или путь относительно текущего каталога. Также файл *.umount хранит версию UNIGINE SDK, в которой было создано монтирование, и информацию о том, доступно ли монтирование только для чтения или нет. Вы также можете указать exclusive_filters (белый список) или ignore_filters (черный список) в качестве списка подстановочных знаков (например, "*.jpg", "some_folder_*"), чтобы выборочно добавлять файлы определенных типов или содержимое папок с определенными именами или, наоборот, игнорировать указанные). Например:
{
"data_path": "D:/mount_test",
"readonly": false,
"ignore_filters": ["*.jpg","*.prop"],
"version": "2.9.0.0"
}
Все папки внутри точки монтирования обрабатываются файловой системой как обычные папки с активами внутри каталога data.
Внутри каждой точки монтирования есть папка .runtimes, в которой хранятся файлы времени выполнения, созданные для ресурсов внешнего каталога. Обратите внимание, что они не добавляются к средам выполнения, хранящимся в папке data/.runtimes. Если вы переместите актив из одной точки монтирования в другую, его среда выполнения также будет перемещена.
GUID для внешних файлов не записываются в data/guids.db.
Если несколько членов команды работают с одной точкой монтирования, она должна быть только для чтения , чтобы избежать проблем.
Точка подключения только для чтения не позволяет вносить изменения в папку или пакет, на которые она ссылается. Это означает, что в такой папке должны храниться активы с уже созданными файлами .meta и средами выполнения. В противном случае они не будут доступны в браузере объектов. Рабочий процесс здесь должен быть следующим:
- .meta и файлы времени выполнения для ресурсов создаются один раз и сохраняются / фиксируются в папке / репозитории (если есть).
- В каждом проекте, в котором используются ресурсы из этой папки / репозитория, создается точка монтирования только для чтения. Ресурсы используются "как есть", без возможности их каким-либо образом изменить.
При работе с точками монтирования следует соблюдать следующие правила:
- Точки монтирования могут быть встроены: папка, на которую ссылается файл *.umount, может хранить другой файл *.umount и т. д. Однако зацикленные точки монтирования не допускаются: вы не можете создать 2.umount внутри 1.umount, который ссылается на 1.umount.
- Файлы *.umount не могут быть упакованы, а также пакеты не могут хранить другие пакеты. Однако файл *.umount может относиться к пакету.
- Файл *.umount должен иметь уникальное имя. Если папка data содержит папку с тем же именем, что и точка монтирования, эта точка монтирования будет проигнорирована.
Когда загружен UnigineEditor, автоматическая перезагрузка ресурсов недоступна для точек монтирования. Каждая точка монтирования обновляется вручную по запросу: в Asset Browser щелкните правой кнопкой мыши точку монтирования и выберите Refresh Mount Point. Когда UnigineEditor не загружен, Engine перезагружает все ресурсы, включая точки монтирования, после перезагрузки мира , если ресурсы добавлены в виртуальную файловую систему.
Пути#
Движок принимает относительный, абсолютный , сетевой и виртуальный пути.
Виртуальные пути#
Файловая система UNIGINE строгая . Это означает, что виртуальная файловая система всегда проверяет точное местоположение файла, а не ищет где-нибудь внутри каталога data. Такой подход делает работу с файлами проекта понятной и прозрачной.
Виртуальная файловая система работает с виртуальными путями к файлам. Виртуальный путь - это путь к файлу внутри папки data (включая файлы в точках монтирования ). Движок всегда пытается преобразовать любой путь в виртуальный. Есть несколько типов виртуальных путей:
- Полный виртуальный путь - путь к файлу внутри папки data
- Частичный виртуальный путь
- Виртуальный путь указан как абсолютный один
При указании виртуального пути к файлу внутри точки монтирования он всегда включает имя точки монтирования. Например, если у вас есть точка монтирования data/external_images.umount, которая относится к D:\external_content, вы должны получить доступ к любому файлу в этой папке следующим образом:
external_images/1.tga
Частичные пути#
Использование частичных путей означает, что файловая система выполняет нестрогий поиск файлов. В этом случае можно указать только имя файла без пути. Частичные пути разрешены в случаях, когда пользователь может ввести путь вручную, например:
- Мировые операции по загрузке. Вы можете указать только имя мира, и оно будет найдено и загружено.
- Включение путей в исходный код.
- Пути в файлах пользовательского интерфейса .
- Пути в файлах материалов руководства .
- Пути к текстурам в файлах базового материала .
- Пути к файлам шрифтов.
Также можно указать вложенный путь, который однозначно определяет файл. Например, чтобы загрузить data/project/my_world/my_world.world, вы можете использовать my_world.world (если имя уникальное) или my_world/my_world.world.
Также вы можете ссылаться на файлы по GUID , чтобы однозначно указать файл.
Виртуальный путь задан как абсолютный#
Движок может разрешать пути, которые указаны как абсолютные, но фактически являются виртуальными. Это означает, что путь к файлу может выглядеть как абсолютный, но физически такого файла по этому пути нет.
Например, в папке D:/projects/unigine_project/ хранится проект. В папке данных этого проекта есть test.umount, который относится к папке D:/content/test/ с текстурой 1.tga. Вы можете указать путь к этой текстуре как абсолютный, как показано ниже, и движок сможет вернуть для нее виртуальный путь.
getVirtualPath("D:/projects/unigine_project/test/1.tga"); // returned: test/1.tga
Если вы попытаетесь получить абсолютный путь, будет возвращен абсолютный путь D:/content/test/1.tga.
getAbsolutePath("D:/projects/unigine_project/test/1.tga"); // returned D:/content/test/1.tga
Чтобы получить больше примеров использования методов getVirtualPath() / getAbsolutePath(), ознакомьтесь со статьей о классе FileSystem.
Сетевые пути#
Большое количество тяжелого контента, используемого в проекте, обычно хранится на сетевом диске. Для доступа к такому контенту необходимо указать сетевой путь в следующем формате:
//share/content/1.tga
Сетевые пути успешно разрешены файловой системой.
Вы можете создать точку монтирования, которая ссылается на сетевую папку. Это позволяет избежать ненужного копирования ресурсов на локальный компьютер и, следовательно, экономит место на диске.
Относительный vs Абсолютный#
относительный путь - это путь, указанный относительно текущего каталога. Его следует использовать, например, когда вам нужно записать какой-либо файл в ту же папку, что и исполняемый двоичный файл. Если вы укажете виртуальный путь, по умолчанию он будет записан в каталог data.
Когда используются относительные пути, вы можете переместить приложение на основе UNIGINE или скопировать его на другой компьютер, и все ресурсы будут правильно загружены. Также нет потери скорости загрузки: это так же быстро, как загрузка файлов по абсолютному пути, благодаря виртуальной файловой системе . Можно использовать абсолютные пути для загрузки ресурсов вне папки data, но такой проект не будет переносимым.
Поскольку имена файлов добавляются в виртуальную файловую систему , обычно следует использовать то же имя и путь для загрузки и удаления файла при доступе из исходного кода с помощью FileSystem функции:
- Для ресурсов по умолчанию функции возвращают полные пути относительно папки data.
- Если вы загружаете файл и указываете относительный путь, используйте относительный путь для удаления ресурса.
- Если вы загружаете файл с использованием абсолютного пути, используйте абсолютный путь для удаления ресурса.
Вы можете проверить, является ли путь абсолютным или относительным, с помощью функции isAbsolute().
Файловая система также позволяет вам получить путь к файлу относительно папки data с помощью функции getVirtualPath().
Приоритеты загрузки#
Движок разрешает любой путь следующим образом:
- Он пытается преобразовать путь в полный виртуальный.
- Он пытается получить текущую точку монтирования по этому пути.
Используя полученную информацию о пути, Engine может получить реальный путь к файлу на диске (или blob / package / cache).
Виртуальный путь может представлять до четырех объектов одновременно: это может быть файл на диске, файл, хранящийся в пакете , файл, добавленный в кеш и к blob .
Например, путь textures/white.texture имеет только один GUID, однако он может одновременно представлять следующее:
- В папке проекта вы можете иметь как core/textures/white.texture, так и core.ung/textures/white.texture.
- Вдобавок в коде вы можете иметь и то, и другое:
// the file loaded into a cache FileSystem::addCacheFile("core/textures/white.texture"); // the file loaded into a blob FileSystem::addBlobFile("core/textures/white.texture");
Во время операций чтения / записи Engine загрузит первый найденный объект для такого виртуального пути. Сущности проверяются в следующем порядке:
- Для операций чтения :
- Файл загружен в большой двоичный объект.
- Файл добавлен в кеш.
- Файл только для чтения, хранящийся на диске.
- Файл, хранящийся в пакете.
- Для операций написать :
- Файл загружен в большой двоичный объект.
- Файл, хранящийся на диске.
Кэшированные и упакованные файлы не проверяются, поскольку для них запрещены операции записи .
Доступ к ресурсам и файлам времени выполнения#
Работа с активами через UnigineEditor проста и понятна, но для правильного доступа к файлам проекта вы должны иметь четкое представление о концепции файлов ресурсов и времени выполнения .
Созданные файлы среды выполнения имеют постоянные идентификаторы GUID и имеют следующие имена:
<GUID>.<extension> (например, ab23be4cd7832478edaaab23be4cd7832478edaa.texture).
Эти файлы хранятся в подпапках папки data/.runtimes . Структура этой папки оптимизирована для файловой системы.
Файл среды выполнения, сгенерированный для неродного ресурса с определенным GUID , помещается в папку с именем, равным первым двум байтам этого GUID.
Например, для вашего неродного ресурса data/my_textures/1.tga будет создан файл времени выполнения (с именем, соответствующим GUID среды выполнения) в папке: ./runtimes/ae/aeb53b44cdbbbbbbbbaaabccc1c1c1c1c1c1c1c1.texture
, поэтому каждый исполняемый файл имеет псевдоним - удобочитаемую форму пути, используемого для ссылки на этот файл.
Полные псевдонимы строятся следующим образом: <source_asset_path>/<runtime_alias>
НАПРИМЕР:
- 1.tga/1.texture
- 1.fbx/1.mesh
Для упрощения доступа к файлам среды выполнения мы также используем концепцию основной среды выполнения - файла среды выполнения, однозначно связанного с активом. Он действует как подразумеваемая ссылка на исполняемый файл: когда мы говорим «model.fbx», мы на самом деле имеем в виду «model.node» . Итак, чтобы мы могли написать:
NodeReferencePtr node = NodeReference::create("model.fbx");
Есть два способа получить доступ к своим ресурсам и файлам времени выполнения:
Файловая система включает подсистему для управления активами и исполняемыми файлами. Эта подсистема реализована как отдельный класс с именем FileSystemAssets.
Вы можете использовать консольные команды assets_info и assets_list для просмотра информации о неродных активах и сгенерированных для них средах выполнения.
Доступ по пути#
Способ доступа к определенному активу по пути определяется его типом:
-
Собственные ресурсы доступны просто по их имени:
ImagePtr image = Image::create("image.texture");
-
Все неродные ресурсы имеют файл основной среды выполнения . Итак, когда вы ссылаетесь на актив по его имени, фактически будет использоваться этот основной файл времени выполнения. Например, если вы укажете:
ImagePtr image = Image::create("image.png");
Фактически будет использоваться сгенерированный image.texture основной файл времени выполнения.
Вы также можете получить прямой доступ к любому исходному файлу ресурса (не к файлу времени выполнения). Например, если вам нужно указать текстуру .png, вы должны написать следующее:
ImagePtr image = Image::create("asset://image.png");
В этом случае исполняемый файл .texture будет проигнорирован, будет использован исходный файл .png.
-
Каждый ресурс контейнера также имеет основную среду выполнения, в случае актива FBX это сгенерированный файл .node. Итак, вы можете использовать следующую ссылку:
NodeReferencePtr node = NodeReference::create("teapot.fbx");
В этом случае будет использоваться сгенерированный файл времени выполнения teapot.node.
Вы можете получить доступ к каждому исполняющему файлу ресурса контейнера. Например, для файла FBX созданы файлы времени выполнения .node и .mesh. Вы можете получить доступ к сгенерированному файлу .mesh следующим образом:
MeshPtr mesh = Mesh::create("teapot.fbx/teapot.mesh");
Доступ по GUID#
Вы также можете получить доступ к любой среде выполнения или активу в своем проекте, используя GUID . Если указан GUID файла, используется точный путь, соответствующий этому GUID:
UGUID asset_guid; // GUID of the asset named "1.tga"
const char *asset_path = "1.tga";
ImagePtr image = Image::create(asset_guid); // -> 1.tga
ImagePtr image = Image::create(asset_path); // -> 1.texture
Модификаторы#
Модификаторы файла служат для автоматического выбора ресурсов для загрузки, когда проект UNIGINE выполняется на разных платформах или с разными локализациями. Вместо того, чтобы хранить несколько версий одного и того же проекта и копировать общие данные между ними, вы можете добавить собственный постфикс к именам файлов или папок и загружать только необходимые ресурсы по запросу.
Модификаторы добавляются к именам файлов или папок в виде постфикса (можно указать только один). Можно использовать любой собственный постфикс. Например, это может быть:
- File name modifier: file.small.node or texture.eng.texture
- Модификатор имени папки: textures.lowres
Если у папки есть модификатор, файлы внутри нее не должны иметь собственных модификаторов. В противном случае модификаторы файлов будут проигнорированы.
Зарегистрируйте необходимые модификаторы в коде через addModifier() . Когда проект запущен, ресурсы с зарегистрированными модификаторами загружаются автоматически. Файлы без модификаторов имеют самый низкий приоритет (могут использоваться для ресурсов по умолчанию).
Пример использования#
Например, в проекте поддерживаются три языка локализации: английский (по умолчанию), немецкий и французский. В зависимости от языка при запуске должны быть загружены разные текстуры заставки.
Чтобы организовать свои ресурсы, назовите их, используя следующие модификаторы файлов:
-
data
-
splashes
- splash.png (this is a default version of the texture. In our case, a texture with an English title)
- splash.de.png (a German title)
- splash.fr.png (a French title)
-
splashes
После этого нужно указать в коде, какой модификатор использовать через addModifier(). Эта функция вызывается в системном скрипте , поскольку модификатор должен быть зарегистрирован до того, как мир и его ресурсы начнут загружаться. Например, чтобы загрузить немецкий заставку и интерфейс текстур с низким разрешением:
// unigine.cpp
int init() {
...
// Register modifier
engine.filesystem.addModifier("de");
// Set a splash texture
engine.splash.setWorld("textures/splash.png"); // splash.de.png will be automatically used
...
return 1;
}
Также вы можете использовать опцию -extern_define CLI для передачи языка (например, если пользователь выбирает язык в пусковой установке).
bin\main_x64d.exe -extern_define "LANG_DE"
А вот как переданные определения могут быть обработаны в коде.
// unigine.cpp
string lang = "";
int init() {
...
// Parse EXTERN_DEFINE
#ifdef LANG_DE
lang = "de";
#elif LANG_FR
lang = "fr";
#endif
if(lang != "") {
engine.filesystem.addModifier(lang);
}
// Set a splash texture: splash.de.png or splash.fr.png will be used if the language is passed
engine.splash.setWorld("textures/splash.png"); // otherwise, splash.png
...
return 1;
}
Асинхронная загрузка#
UNIGINE Engine позволяет управлять асинхронной загрузкой файлов с помощью класса AsyncQueue. Все методы этого класса, связанные с файлами, загружают файл и добавляют его в файловую систему как кэшированный.