文件系统
UNIGINE具有其自己的文件系统模块,该模块用于访问文件和文件夹。在加载资源或组织基于UNIGINE的项目的结构时,应该注意一些特殊之处。
图形用户界面#
在UNIGINE的文件系统中,每个文件都有一个 GUID (全局唯一标识符),该文件定义了此文件(不是磁盘上的文件)的虚拟路径。使用GUID提供了更灵活的文件管理:您可以从文件名中提取文件名(在项目的不同文件夹中可以相同)。例如,您可以在保留相同GUID的情况下更改文件的路径。
引擎为虚拟文件系统的所有文件生成GUID。
可以使用名称或GUID来访问UNIGINE文件系统的文件:您可以获取特定文件的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文件中搜索。但是,如果您不打算包含.meta文件,则最终版本的项目可能需要使用guids.db。
忽略guids.db的选项#
有时在团队合作开发的情况下,由于与 UnigineEditor 一起使用,可能将无效的guids.db文件提交到存储库(由于合并错误或其他原因),从而导致错误。您可以通过 -skip_guidsdb 启动命令行选项使Engine忽略guids.db文件。在这种情况下,引擎将在data文件夹内的所有.meta文件以及所有已安装外部目录和程序包中搜索GUID。默认情况下,UnigineEditor使用此参数来避免错误,并始终重新生成guids.db文件以确保其有效性。
文件系统更新#
动态扫描#
动态扫描允许引擎为data文件夹中的所有文件(包括装载点中的文件)形成一个虚拟文件系统。动态扫描在引擎启动时执行。对于物理存储在data文件夹中的文件,使用指向地址文件的相对路径实时启用文件更改跟踪。
如果不是通过UnigineScript对文件系统进行更改,则可能需要调用 filesystem_reload 控制台命令。
自动资源重装#
加载UnigineEditor时,引擎会在运行时跟踪文件中所做的更改:它会检查上次修改文件的时间并在内存中进行更新。如果未加载UnigineEditor,则在重新加载世界后重新加载已更改的文件。
已知文件与未知文件#
如果您在运行时添加新文件,则引擎将不了解有关此类文件的任何信息(因为虚拟文件系统已在启动时形成)。重新扫描文件系统会占用大量资源,因此,您可以使用 addVirtualFile() 通过API将新文件添加到虚拟文件系统。
资料目录#
引擎在运行时使用的所有文件都存储在通过 -data_path 启动选项指定的data文件夹中。默认情况下,这是通过UNIGINE SDK Browser创建项目时自动创建的data文件夹。
文件系统解析路径时,它将尝试将数据路径与指定的路径连接起来并执行查找。对于绝对路径,文件系统将按原样使用它们,而无需进行任何检查。
例如,如果项目文件夹具有以下结构:
-
unigine_project
- bin
- data
应用程序启动后,数据路径将为unigine_project/data:
bin\main_x64d.exe -data_path "../"
当前目录#
如果指定的 -data_path 是绝对的,则当前工作目录可能与具有二进制可执行文件的目录不同。但是,当数据目录的路径是相对路径时,引擎会将当前目录切换为带有二进制可执行文件的目录。
通过API访问data目录之外的文件时,应相对于当前目录指定该文件的路径。例如:
// cbox.mesh is stored outside the data directory, so the path is specified relative to the current directory
ObjectMeshStatic cbox = new ObjectMeshStatic("../../data/cbox.mesh");
根挂载文件#
虚拟文件系统的构建块是装载:文件系统创建为根装载,并且可以通过使用其他装载点轻松扩展。这种方法允许通过将任何外部文件夹和软件包添加到数据目录来扩展项目的虚拟文件系统。
在创建项目时,将在data文件夹中创建root_mount.umount文件。这是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和ZIP归档文件如果位于data文件夹中,则会自动加载。文件与未归档文件的添加方法一样,被添加到虚拟文件系统。
内容访问#
存档对引擎完全透明。无需显式解压缩归档文件,因为它们的内容会自动处理为未打包。存档文件的处理就像未存档一样。例如,如果您有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档案中的文件
- ZIP存档中的文件
通过UNIGINE API,也可以使用FilesSystem函数处理存档。
扩展文件系统#
通过使用安装点功能可以轻松扩展虚拟文件系统。它允许您通过将任何外部文件夹和软件包添加到数据目录来扩展项目的虚拟文件系统。
使用项目中的挂载点可以使用存储的内容:
- 在用于多个项目的单个文件夹或存储库中。
- 在一个项目的多个文件夹中。您可以根据项目需要创建任意数量的装载点。
基于UNIGINE的项目只有一个data目录。此处存储了所有项目资产和运行时文件。另外,data文件夹可以存储通过资源浏览器(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文件夹中存储的运行时中。如果将资产从一个挂载点移动到另一个挂载点,其运行时间也会随之移动。
和运行时文件分别按安装点存储 一样,默认情况下,还将按安装点生成并存储guids.db文件:外部文件的GUID不会写入data/guids.db。
如果多个团队成员使用一个安装点,则应该为只读,以避免出现问题。
只读安装点不允许对其引用的文件夹或软件包进行任何更改。这意味着该文件夹必须存储具有已生成的.meta文件和运行时的资产。否则,它们将不会在资源浏览器中提供。此处的工作流程应如下:
- 资产的.meta和运行时文件生成一次,然后保存/提交到文件夹/存储库(如果有)。
- 在使用该文件夹/存储库中资产的每个项目中,将创建只读安装点。资产按“原样”使用,没有机会以某种方式进行修改。
使用安装点时,需要遵循以下规则:
- 可以嵌入挂载点:*.umount文件引用的文件夹可以存储另一个*.umount文件,依此类推。但是,不允许循环挂载点:您不能在引用1.umount的1.umount内创建2.umount。
- *.umount文件无法打包,并且程序包无法存储其他程序包。但是,*.umount文件可以引用一个包。
- *.umount文件应具有唯一的名称。如果data文件夹包含与挂载点名称相同的文件夹,则将忽略此挂载点。
加载UnigineEditor时,自动资源重新加载对安装点不可用。每个安装点均根据需要手动更新:在资源浏览器中,右键单击安装点,然后选择Refresh Mount Point。如果未将UnigineEditor加载到虚拟文件系统中,则在重新加载世界后,引擎将重新加载所有资源,包括安装点。
路径#
虚拟路径#
UNIGINE的文件系统为 strict 。这意味着虚拟文件系统始终检查确切的文件位置,而不是在data目录中的某个位置进行搜索。这种方法使处理项目文件清晰透明。
虚拟文件系统使用虚拟路径来操作文件。虚拟路径是data文件夹中文件的路径(包括装载点中的文件)。引擎始终尝试将任何路径转换为虚拟路径。虚拟路径有几种类型:
在挂载点内指定文件的虚拟路径时,它始终包含挂载点的名称。例如,如果您有一个指向D:\external_content的data/external_images.umount挂载点,则应按以下方式访问此文件夹中的任何文件:
external_images/1.tga
部分的#
使用部分路径意味着文件系统执行非严格文件搜索。在这种情况下,只能提供没有路径的文件名。在用户可以手动输入路径的情况下,允许使用部分路径,例如:
- 世界装载作业。您只能指定一个世界名称,它将被找到并加载。
- 在源代码中包含路径。
- UI文件中的路径.
- 手动材料文件中的路径。
- 基础材料文件中的纹理路径。
- 字体文件的路径。
也可以提供唯一指定文件的子路径。例如,要加载data/project/my_world/my_world.world,可以使用my_world.world(如果名称是唯一的)或my_world/my_world.world。
您还可以通过 GUID 引用文件以唯一地指定文件。
虚拟路径被指定为绝对路径#
引擎可以解析指定为绝对路径,但实际上是虚拟路径。这意味着文件的路径看起来像是绝对路径,但实际上该路径没有该文件。
例如,有一个项目存储在D:/projects/unigine_project/文件夹中。在该项目的数据文件夹中,有一个test.umount引用了具有1.tga纹理的D:/content/test/文件夹。您可以按如下所示将此纹理的路径指定为绝对路径,然后引擎将能够为其返回虚拟路径。
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()函数检查路径是绝对路径还是相对路径。
文件系统还允许您使用getVirtualPath()函数获取相对于data文件夹的文件路径。
加载优先级#
引擎按以下方式解析任何路径:
- 尝试将路径转换为完整的虚拟路径。
- 尝试通过此路径获取当前安装点。
使用获得的路径信息,引擎可以获取磁盘(或blob / package / cache)上文件的真实路径。
虚拟路径最多可以同时表示四个实体:它可以是磁盘上的文件,存储在 package 中的文件,添加到 cache 并链接到 blob 。
例如,textures/white.dds路径只有一个GUID,但是它可以同时表示以下内容:
- 在项目文件夹中,您可以同时拥有core/textures/white.dds和core.ung/textures/white.dds。
- 此外,在代码中,您可以同时拥有:
// the file loaded into a cache FileSystem::addCacheFile("core/textures/white.dds"); // the file loaded into a blob FileSystem::addBlobFile("core/textures/white.dds");
在读/写操作期间,引擎将为该虚拟路径加载第一个找到的实体。按照以下顺序检查实体:
- 对于读取操作:
- 加载到Blob中的文件。
- 已将文件添加到缓存中。
- 存储在磁盘上的只读文件。
- 文件存储在包中。
- 对于写入操作:
- 加载到Blob中的文件。
- 存储在磁盘上的文件。
不允许检查缓存和打包的文件,因为不允许对其执行 write 操作。
访问资产和运行时文件#
通过UnigineEditor处理资产非常简单明了,但是要正确访问项目文件,您应该对资产和运行时文件的概念有清楚的了解。
生成的运行时文件具有恒定的GUID,其名称如下:
<GUID>.<extension>(例如ab23be4cd7832478edaaab23be4cd7832478edaa.dds)。
这些文件存储在 data/.runtimes 文件夹的子文件夹中。结构或此文件夹针对文件系统进行了优化。
为具有特定 GUID 的非本地资产生成的运行时文件放置在一个名称等于该GUID的前两个字节的文件夹中。
例如,您的非本地资产data/my_textures/1.tga将在文件夹./runtimes/ae/aeb53b44cdbbbbbbbbaaabccc1c1c1c1c1c1c1c1.dds中为其生成运行时文件(名称与运行时的GUID相对应)
因此,每个运行时文件都有一个 alias (别名)-用于引用该文件的路径的可读形式。
完整别名的构造如下:<source_asset_path>/<runtime_alias>
例如:
- 1.tga/1.dds
- 1.fbx/1.mesh
为了简化对运行时文件的访问,我们还使用了主要运行时的概念-与资产唯一关联的运行时文件。它就像对运行时文件的隐式引用:当我们说“ model.fbx”时,实际上是指“ model.node” 。因此,我们可以这样写:
NodeReferencePtr node = NodeReference::create("model.fbx");
您可以通过两种方式访问资产和运行时文件:
文件系统包括一个用于管理资产和运行时文件的子系统。该子系统被实现为一个单独的类,名为FileSystemAssets。
可以使用assets_info和assets_list控制台命令来查看有关非本地资产和为其生成的运行时的信息。
按路径访问#
通过路径访问特定资产的方式取决于其类型:
-
本地资产 只需按其名称即可访问:
ImagePtr image = Image::create("image.dds");
-
所有 非本地资产 都有一个主要运行时文件。因此,当您通过资产名称引用资产时,将实际使用该主运行时文件。例如,如果您指定:
ImagePtr image = Image::create("image.png");
实际将使用image.dds生成的主运行时文件。
您也可以直接访问任何资产源文件(而不是运行时文件)。例如,如果需要指定.png纹理,则应编写以下内容:
ImagePtr image = Image::create("asset://image.png");
在这种情况下,运行时.dds文件将被忽略,而.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.dds
修饰符#
文件修改器用于在UNIGINE项目在不同平台或具有不同本地化版本上运行时自动选择要加载的资源。您可以在文件或文件夹名称中添加自定义后缀,然后仅按需加载所需的资源,而不必保留同一项目的多个版本并在它们之间复制共享数据。
修饰符作为后缀添加到文件或文件夹名称中(只能指定一个)。可以使用任何自定义的后缀。例如,它可能是:
- 文件名修饰符:file.small.node或texture.eng.dds
- 文件夹名称修饰符:textures.lowres
如果文件夹具有修饰符,则其中的文件不应具有自己的修饰符。否则,文件修饰符将被忽略。
通过 addModifier() 在代码中注册必要的修饰符。当项目运行时,带有已注册修饰符的资源会自动加载。没有修饰符的文件具有最低优先级(可以用于默认资源)。
使用范例#
例如,项目中支持三种本地化语言:英语(默认),德语和法语。根据语言,应在启动时加载不同的启动纹理。
要组织资源,请使用以下文件修饰符对其进行命名:
-
data
-
splashes
- splash.png (这是纹理的默认版本。在我们的例子中,是带有英文标题的纹理)
- splash.de.png (德语标题)
- splash.fr.png (法文标题)
-
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类控制文件的异步加载。此类的所有与文件有关的方法都将加载文件,并将其作为缓存的文件添加到文件系统中。