UE开发
UE的LoadObject接口底层机制
UE的LoadObject接口底层机制详解
最近在看 UE 的 Pak 加载流程时,有一个地方比较容易绕进去:LoadObject 传入的明明只是 /Game/... 这样的对象路径,最后却能从已经挂载的 Pak 中把模型加载出来。要理解这件事,关键是先把 UObject、UPackage、Cook 后的资源文件和 .pak 区分开。

UE 创建资产时,通常会先创建一个 UPackage,再把具体的 UObject 创建到这个 Package 下。例如:
UPackage* Package =
CreatePackage(TEXT("/Game/BIM/SM_3D"));
UStaticMesh* Mesh =
NewObject<UStaticMesh>(
Package,
TEXT("SM_3D")
);
这样生成的 StaticMesh 对象路径就是:
/Game/BIM/SM_3D.SM_3D
前半部分 /Game/BIM/SM_3D 是 PackageName,后面的 SM_3D 是 ObjectName。这里的 UPackage 只是 UE 在逻辑上组织 UObject 的单位,并不是最后生成的 .pak 文件。
资产保存并经过 Cook 后,会生成运行时真正需要读取的 Package 文件,例如:
SM_3D.uasset
SM_3D.uexp
SM_3D.ubulk
之后 UnrealPak 再把这些 Cook 后的文件收集到 .pak 中,同时可以做压缩、加密以及建立文件索引。因此整个打包过程可以简单看成:
UObject
→ UPackage
→ Cook
→ uasset / uexp / ubulk
→ UnrealPak
→ xxx.pak
运行时调用:
LoadObject<UStaticMesh>(
nullptr,
TEXT("/Game/BIM/SM_3D.SM_3D")
);
UE 首先会解析对象路径,得到:
PackageName = /Game/BIM/SM_3D
ObjectName = SM_3D
/Game 是 UE 的逻辑 Package 根路径,通常对应当前项目的 Content 目录,所以 /Game/BIM/SM_3D 最终会对应到类似:
<Project>/Content/BIM/SM_3D.uasset
到这里其实还没有涉及 Pak。Package Loader 只是知道自己需要读取 BIM/SM_3D 这个 Package,接下来才会把请求交给底层文件系统。
Pak 的 MountPoint 就是在这里起作用的。假设 BIM.pak 中保存了:
BIM/SM_3D.uasset
BIM/SM_3D.uexp
运行时将这个 Pak 挂载到:
<Project>/Content/
那么从 UE 文件系统的角度看,就相当于出现了:
<Project>/Content/BIM/SM_3D.uasset
<Project>/Content/BIM/SM_3D.uexp
这些文件并没有真的解压到磁盘目录中,数据仍然保存在 BIM.pak 里。MountPoint 只是建立了一层虚拟路径关系:
MountPoint + Pak 内相对路径
=
UE 文件系统看到的路径
因此当 Package Loader 请求读取:
<Project>/Content/BIM/SM_3D.uasset
时,请求会进入 IPlatformFile 文件系统。如果当前使用的是 Pak 文件系统,就会经过 FPakPlatformFile。它会检查已经挂载的 Pak,发现这个请求路径落在 BIM.pak 的 MountPoint 下,于是转换成 Pak 内对应的相对路径:
BIM/SM_3D.uasset
随后通过 Pak Index 找到这个文件在 .pak 中对应的 Offset、Size 以及压缩块信息,再直接从 Pak 中读取数据。如果文件经过压缩,就先解压,然后把最终字节交回 Package Loader,最后反序列化成 UStaticMesh。
整个过程可以概括成:
LoadObject("/Game/BIM/SM_3D.SM_3D")
↓
解析 PackageName
↓
/Game/BIM/SM_3D
↓
映射为 Package 文件路径
↓
Content/BIM/SM_3D.uasset
↓
IPlatformFile 文件读取
↓
FPakPlatformFile
↓
根据 MountPoint 匹配已挂载 Pak
↓
Pak Index 查找文件
↓
从 Pak 中读取数据
↓
Package Loader 反序列化
↓
UStaticMesh
这里最容易混淆的是 UPackage 和 .pak。UPackage 是 UObject 的逻辑组织单位,而 .pak 只是 Cook 后文件的归档容器。另外,LoadObject 本身并不知道资源最终来自 Pak,它只负责根据对象路径找到对应 Package;真正决定文件是从磁盘读取还是从 Pak 读取的是底层文件系统。
所以从整体上看,UE 这一套加载机制实际上就是:
对象路径
→ Package 路径
→ 文件路径
→ Pak 虚拟文件系统
→ 二进制数据
→ UObject
也正因为这一层做了隔离,Pak 挂载完成之后,上层仍然可以继续使用普通的 LoadObject,不需要专门提供一个 LoadObjectFromPak。
评论 (1)
参与讨论,分享你的看法。评论审核通过后展示。
🐂