前段时间,由于工作需要,研究了一下如何挂载Pak以及在热更应用中的思考,包括新增的Pak和替换旧Pak。在查阅大量资料和亲手实践后,在此做个总结记录。
前置知识
Pak的概念、制作、挂载等前置知识,可以查看下列参考文档:
Pak文件的优先级
- 挂载的顺序并不是查找的顺序,每当有新文件挂载时,内部的pak列表会根据优先级来排序。优先级大的排在前面,优先被查找。
- 优先级规则
i.规则1:基于文件路径
"[ProjectName]/Content/Paks/[ProjectName]-XXX"的pak文件优先级为4
"[ProjectName]/Content/Paks" 目录下除上述pak文件的优先级为3
"Engine/Content/Paks"目录优先级为2
"[ProjectName]/Saved/Paks" 目录优先级为1
其余为0
ii.规则2:基于文件名
文件名以"_P.pak"结尾的Pak享有额外的优先级,最低为100。
“_n_P.pak”结尾的优先级 =(n+1)*100+规则1 的优先级
iii.规则3:手动挂载Pak时可以指定优先级
挂载Pak
- 挂载,只是读取pak中的文件列表信息。加载时会按照Pak优先级去查找文件。
注意:要在挂载完所需Pak之后,再加载所需资源。例如:pak1.pak里有一个蓝图Actor,pak2.pak里是稍作修改的Actor。如果挂载pak1.pak,加载Actor,再挂载pak2.pak,再加载Actor,这种顺序就只能加载到pak1中的Actor。
- 引擎会自动从以下三个目录挂载Pak:
[ProjectName]/Content/Paks
[ProjectName]/Saved/Paks
Engine/Content/Paks
1.挂载新增的Pak
方法1:将Pak放到引擎自动挂载的目录下(若开启IO,需要包含.pak/.utoc/.ucas
三个文件)。如果是用UnrealPak打的Pak包,需要将挂载点一起打进包里,才能在自动挂载后正确加载包里的资源。
方法2:手动挂载
2.挂载新Pak,替换旧Pak
方法1:直接替换掉旧Pak文件。若开启IO,需要将.pak/.utoc/.ucas一起替换。
方法2:将Pak放到引擎自动挂载的目录下,并根据优先级规则调整文件名来使优先级高于旧Pak。同样需注意Pak包里包含正确挂载点。若开启IO,需要将.pak/.utoc/.ucas一起重命名。
方法3:手动挂载
3.手动挂载Pak
3.1注意事项:
- 打包模式使用 Cooked 过的 Pak
- PIE 模式使用未 Cooked 过的 Pak
- 打包模式,MountPoint 使用相对路径,即../../../[项目名]/Content/[子目录名]
- PIE模式,MountPoint 使用绝对路径,即FPaths::ProjectContentDir() + TEXT("[Content后的子目录]/")
- 需要切换到Pak平台下,才能加载Pak包中的资源
- Cook路径和挂载点路径尽量保持一致,否则容易导致依赖丢失
- 若开启IO,需要将.pak/.utoc/.ucas放在相同目录下,只有.pak是无法正确挂载的。
- 可以挂载与包里同名的Pak
3.2核心代码示例
示例情境:
- 工程E:\UE5Projects\PakDemo\Content\Pak1下有一个蓝图Actor1,且关闭IO。
- Cook后,使用UnrealPak将E:\UE5Projects\PakDemo\Saved\Cooked\Windows\PakDemo\Content\Pak1下的资源打包到G:\Actor1.pak。
- 通过代码手动挂载Actor1.pak,加载蓝图Actor1并创建实例
-
//.Build.cs 添加 PakFile 模块 //获取当前使用的平台 InnerPlatformFile = &FPlatformFileManager::Get().GetPlatformFile(); InnerPlatformFile->GetName()); //初始化PakPlatformFile PakPlatformFile = MakeShareable(new FPakPlatformFile()); PakPlatformFile.Get()->Initialize(InnerPlatformFile, TEXT("")); // 切换到Pak平台 FPlatformFileManager::Get().SetPlatformFile(*PakPlatformFile.Get()); // 获取Pak文件(PakPath = “G:\Actor1.pak” ) TRefCountPtr<FPakFile> PakFile = new FPakFile(InnerPlatformFile, *PakPath, false); FString MountPoint = PakFile->GetMountPoint(); //按需调整挂载点,若Pak包里包含正确挂载点,则无需调整 //PIE模式 //MountPoint = FPaths::ProjectContentDir() + TEXT("Pak1/"); //打包模式 MountPoint = TEXT("../../../PakDemo/Content/Park1"); //对pak文件进行挂载 //100000为优先级 //若Pak包里自带正确挂载点,*MountPoint可不传 if (PakPlatformFile->Mount(*PakPath, 100000, *MountPoint)) { //加载Actor1并创建实例 UClass* BP_PakTestClass = LoadClass<AActor>(nullptr,TEXT("Blueprint'/Game/Pak1/Actor1.Actor1_C'")); if (BP_PakTestClass) { GetWorld()->SpawnActor<AActor>(BP_PakTestClass, FVector::ZeroVector,FRotator::ZeroRotator); } }