UE4 文件系统
类图
IPlatformFile
文件类的最基础接口,定义了对文件进行操作相关的方法,从这个接口派生的类又分为两大类:
- 物理文件类
- 这个就是我们通常理解的文件类,就是各个平台下直接对文件进行操作的类
- 包装(
Wrapper
)文件类- 这个类型的文件类不会直接对文件进行操作,它对文件的操作是通过自己持有的底层对象进行的,最终各个类型的包装文件类会构成一个文件操作链,这个链上的最后一个对象就是物理文件类对象
- 这个类型的子类实现中都会有个
IPlatformFile*
类型的成员变量,这个其实就是类似我们链表数据结构中的Next
指针对象,指向链表的下一个对象。不过不明白这里为什么不抽象一个类似IPhysicalPlatformFile
的接口,把这个成员变量放到这个基类接口里,省得每个子类里都得写一遍
这个类需要注意一下这些接口:
Initialize
: 初始化接口,对于包装文件类来说,第一个参数是他所指向的下一个文件类对象。对于物理文件类,第一个参数只能是空的; 第二个参数是命令行,部分文件类会从这里去解析一些参数GetPlatformPhysical
: 这个接口直接返回当前平台对应的物理文件类对象,这个函数的实现在各个平台的物理文件类封装中。一般我们通过FPlatformFileManager::GetPlatformFile
返回的是文件操作链的链头。ShouldBeUsed
: 每个包装文件类是否生效的逻辑在这里实现
物理文件类: IPhysicalPlatformFile
这个类是物理文件类的基础接口,主要对 SetLowerLevel
这个接口进行屏蔽,因为物理文件类对象肯定是文件操作链上的最后一个对象,所以这个接口是不让用的
FLinuxPlatformFile
对 Linux
平台的文件操作进行封装
FWindowsPlatformFile
对 Windows
平台的文件操作进行封装
FHTML5PlatformFile
对 HTML5
平台的文件操作进行封装
IAndroidPlatformFile/FAndroidPlatformFile
IAndroidPlatformFile
: 这个类也是个接口类,主要是扩展了几个Android
平台上的接口FAndroidPlatformFile
: 继承自IAndroidPlatformFile
,对Android
平台的文件操作进行封装
FApplePlatformFile/FIOSPlatformFile
FApplePlatformFile
: 对OSX
平台的文件操作进行封装FIOSPlatformFile
: 继承自FApplePlatformFile
,对iOS
平台的文件操作进行封装
包装文件类
入口函数 ConditionallyCreateFileWrapper
FCachedReadPlatformFile
这个包装文件类实现了预读的优化,具体文件的预读逻辑在文件读写类 FCachedFileHandle
中,简单说就是假如读一个文件,读100字节,经过这个类的包装,其实是会读最多64KB的文件内容到内存中,下次再读这个文件时其实就是内存拷贝
FLoggedPlatformFile
这个类会将每一次文件操作的耗时打印到 log
中,并且输入命令 LogFileDump
会将当前打开的文件列表打印到 log
中
FPlatformFileOpenLog
- 记录游戏或者编辑器的读文件历史,结果保存到
EditorOpenOrder.log
或者GameOpenOrder.log
中 - 不能用在
Shipping
版本中
FNetworkPlatformFile/FCookedIterativeNetworkFile/FStreamingNetworkPlatformFile
- 这几个包装器会从服务器上下载文件,比如打开某个文件,这个文件如果不存在,则先会从文件服务器上把这个文件下载到本地,然后再打开
- 优先级是
FStreamingNetworkPlatformFile
>FCookedIterativeNetworkFile
>FNetworkPlatformFile
- 不能用在
Shipping
版本中
FSandboxPlatformFile
- 如果启用了沙盒,那么操作的文件将会转到沙盒内的文件访问
- 注意这个函数
OkForInnerAccess
里面有两个列表,一个是文件夹匹配列表,一个是文件匹配列表,不在这个列表中文件或者文件夹,在沙盒访问失败后,还会用原始路径访问一次
FPakPlatformFile
-
这个包装类很重要, 因为这个就是虚幻中对
pak
文件的读取封装了,了解虚幻中怎么读取pak
文件,可以详细阅读这个类的实现 -
IsNonPakFilenameAllowed
,这个函数控制当从pak
中访问文件失败时,是否继续让底层文件类直接访问目标路径 -
猜想: 这个是不是可以用来做一件事,比如某些资源不打包到
pak
中,然后游戏开始的加载环节中,把这些资源动态地下载下来 -
测试
- 先按正常流程打包游戏,
Content
目录如图
- 然后从另一个游戏工程里复制了一个
cook
好的特效资源,然后放到Content/Test
目录下,如图
- 然后在代码里直接加载测试资源
IPlatformFile& StartNode = FPlatformFileManager::Get().GetPlatformFile(); IPlatformFile* PlatformFile = &StartNode; IPlatformFile* PakPlatformFile = nullptr; do { // 查找 PakPlatformFile UE_LOG(LogTest, Warning, TEXT("Platform file name[%s]."), PlatformFile->GetName()); if (FCString::Strcmp(PlatformFile->GetName(), FPakPlatformFile::GetTypeName()) == 0) { PakPlatformFile = PlatformFile; } PlatformFile = PlatformFile->GetLowerLevel(); } while (PlatformFile); // 确保当前使用了 pak check(PakPlatformFile); // 测试是否能加载到独立的资源 UParticleSystem* PS = LoadObject<UParticleSystem>(nullptr, TEXT("/Game/Test/PS_ForLoadTest.PS_ForLoadTest")); if (PS) { UE_LOG(LogTest, Warning, TEXT("Found PS_ForLoadTest")); } else { UE_LOG(LogTest, Warning, TEXT("Not Found PS_ForLoadTest")); }
- 测试结果如下图:
- 先按正常流程打包游戏,
FPlatformFileReadStats
- 文件读取速度统计
FProfiledPlatformFile
- 文件操作速度统计,类似
FLoggedPlatformFile