这几天弄UE5的Pak包加载,弄得晕头转向,网上都是UE4.27以下的教程,UE4.27, UE5修改了一些东西,导致按照[虚幻官方直播第四期的教程](https://www.bilibili.com/video/BV1Ut411A7sk?spm_id_from=333.337.search-card.all.click&vd_source=f7b2defde4971310a19a6e9e40c36b90)无法成功加载,废话不多说,先说一下踩的坑。
一·UE5加载Pak和UE4不同的地方
1.取消Use Iostore(使用Io保存)的勾选
UE5的打包设置中自动勾选了使用Io保存,还是试用功能,引擎的Mount函数中有对此的判断,
如果启用IO保存,会检测是否存在对应的.utoc文件,如果没有,返回false,Mount失败。
我们自己使用UE5的Cook,打包是不会生成.utoc文件的,导致一直加载失败。至于如何正确使用Iostore之后有时间再看看。
2.取消共享材质着色器代码(Share Material Shader Code)勾选
UE5默认启动共享材质着色器代码,导致加载出来的Actor材质丢失,取消勾选可以解决问题。
3.蓝图调用C++路径名莫名其妙的多了一个空白的字符
这个可能大家没遇到,我遇到了,在调试的时候发现,FString类型的参数莫名其妙多了一个空白的字符,导致一直无法找到Pak包。教程使用命令行调用该函数不存在这个问题,我将参数在C++里写死了,规避了这个问题,以后有时间再看看。
主要踩了这三个坑,下面再说一下整个Pak的使用流程。
二、UE5 Pak包的Cook,打包,加载流程
1.cook
先创建DLC文件夹,有一个Actor,一个贴图,一个材质,一个Mesh,
在Actor里面添加测试代码,每秒打印一次“耶,我被成功加载了”
将DLC文件夹添加到要烘焙的额外资产目录
在平台中启用烘焙,这里要注意,UE4的烘焙按钮是在文件里面,UE5的烘焙按钮在平台
烘焙成功后,可以看到G:\UE5Demo\PakTest\Saved\Cooked\Windows\PakTest\Content\DLC里面有DLC的.uasset文件
2 打Pak包
找到引擎目录的UnrealPak.exe,教程说可以将它不依赖UE的库,可以将它移到任意地方运行,试了一下,不行。老老实实使用cmd运行,cd到该目录下,运行UnrealPak.exe。
然后输入命令unrealpak {pak目录} -create={cook文件的目录} 打pak包
unrealpak G:\dlc.pak -create=G:\UE5Demo\PakTest\Saved\Cooked\Windows\PakTest\Content\DLC
这是普通的打pak的方式,加密或者压缩Pak包我就不演示。
输入命令unrealpak {pak目录} -list 查看pak包
unrealpak G:\dlc.pak -list
3. 加载Pak包
创建一个C++类继承自Actor,在工程build.cs里面添加模块"PakFile"
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "PakFile" });
在.h里声明OldPlatform和PakPlatform,这里不求甚解,不用理会到底是干嘛的,花大代价搞明白了估计也过几天就忘。
声明TestLoadPak函数,这里使用蓝图调用,也可以学习教程使用命令行调用,将UFUNCTION(BlueprintCallable)改为UFUNCTION(Exec)即可。
TSharedPtr<class FPakPlatformFile> PakPlatform; class IPlatformFile* OldPlatform; UFUNCTION(BlueprintCallable) bool TestLoadPak(const FString& InPakFullPath);
在.cpp文件中引用头文件
#include "MyActor.h"#include "IPlatformFilePak.h"#include "HAL/PlatformFilemanager.h"#include "Runtime/Engine/Classes/Engine/StreamableManager.h"#include "Runtime/Engine/Classes/Engine/AssetManager.h"#include "Runtime/Engine/Classes/Engine/StaticMeshActor.h"#include "Kismet/KismetStringLibrary.h"
实现BeginPlay函数,初始化
Super::BeginPlay(); OldPlatform = &FPlatformFileManager::Get().GetPlatformFile(); PakPlatform = MakeShareable(new FPakPlatformFile()); PakPlatform->Initialize(&FPlatformFileManager::Get().GetPlatformFile(), TEXT(""));
实现TestLoadPak函数
bool AMyActor::TestLoadPak(const FString& InPakFullPath){ FPlatformFileManager::Get().SetPlatformFile(*PakPlatform.Get()); FString PakFileFullPath = L"g:/dlc.pak"; if (!FPlatformFileManager::Get().GetPlatformFile().FileExists(*PakFileFullPath)) return false; //FString PakName = GetPakFileName(PakFileFullPath); TRefCountPtr<FPakFile> TmpPak = new FPakFile(PakPlatform.Get(), *PakFileFullPath, false); FString OldPakMountPoint = TmpPak->GetMountPoint(); int32 ContentPos = OldPakMountPoint.Find("Content/"); FString NewMountPath = OldPakMountPoint.RightChop(ContentPos); FString ProjectPath = FPaths::ProjectDir(); //ProjectPath = "../../../PakTest/"; NewMountPath = ProjectPath + NewMountPath; TmpPak->SetMountPoint(*NewMountPath); if (PakPlatform->Mount(*PakFileFullPath, 1, *NewMountPath)) { //StaticMesh'/Game/DLC/SM_Cube.SM_Cube_C' //Blueprint'/Game/DLC/DLC_Cube.DLC_Cube' //World'/Game/ThirdPerson/Maps/ThirdPersonMap.ThirdPersonMap' TArray<FString> FoundFilenames; TmpPak->FindFilesAtPath(FoundFilenames, *TmpPak->GetMountPoint(), true, false, false); if (FoundFilenames.Num() > 0) { if (GetWorld()->WorldType == EWorldType::Game) { for (FString& Filename : FoundFilenames) { if (Filename.EndsWith(TEXT(".uasset"))) { FString NewFileName = Filename; FString PathDir = FPaths::ProjectContentDir(); NewFileName.ReplaceInline(*PathDir, TEXT("/Game/")); FString File = FPaths::GetBaseFilename(Filename); NewFileName.ReplaceInline(TEXT("uasset"), *File); FString blueprint = TEXT("Blueprint'"); NewFileName.Append(TEXT("_C'")); GEngine->AddOnScreenDebugMessage(-1, 20.0f, FColor::Red, *NewFileName); NewFileName=UKismetStringLibrary::Concat_StrStr(TEXT("Blueprint'"), NewFileName); UClass* Class = LoadClass<AActor>(NULL, *NewFileName); //UClass* Class = LoadClass<AActor>(NULL, TEXT("Blueprint'/Game/DLC/DLC_Cube.DLC_Cube_C'")); if (Class == nullptr) { GEngine->AddOnScreenDebugMessage(-1, 20.0f, FColor::Red, TEXT("Load Class Error")); } else { AActor* MeshActor = GetWorld()->SpawnActor<AActor>(Class, FVector(100, 100, 400), FRotator(0, 0, 0)); } } } } } } //设置回原来的读取方式,不然包内的资源可能访问不了 FPlatformFileManager::Get().SetPlatformFile(*OldPlatform); return true;}
这个例子指的注意的是有三个路径,
1.Pak包的路径-PakFileFullPath,我将Pak包的路径写死的原因是之前提到,蓝图传参的时候,莫名其妙多了一个空字符,导致路径一直不对。然后生成一个FPakFile对象,注意UE5将FPakFile的析构函数私有化了,不能使用共享智能指针。
TRefCountPtr<FPakFile> TmpPak = new FPakFile(PakPlatform.Get(), *PakFileFullPath, false);
2.挂载点的路径-NewMountPath,NewMountPath=“…/…/…/PakTest/Content/DLC”,要想办法拼对这个路径,具体需要在打包之后调试一下,查看各个路径的值,最终拼成上述值的结构"…/…/…/{项目名}/Content/{DLC目录}"。然后设置挂载点并挂载
TmpPak->SetMountPoint(*NewMountPath);PakPlatform->Mount(*PakFileFullPath, 1, *NewMountPath)
3.Pak包资源的虚拟路径-NewFileName,我的是"Blueprint’/Game/DLC/DLC_Cube.DLC_Cube_C’",在我们烘培的时候,直接在UE5编辑器选择资源-》复制引用,后面加上”_C“,即可得到该路径。我们也是要把从Pak包里读出的文件名拼成这样的路径。最后通过LoadClass加载虚拟路径的资源,并生成,
UClass* Class = LoadClass<AActor>(NULL, *NewFileName);AActor* MeshActor = GetWorld()->SpawnActor<AActor>(Class, FVector(100, 100, 400), FRotator(0, 0, 0));
最后派生一个Actor,调用该函数,放到场景中,将UE5编辑器的DLC文件夹删除,注意每次执行删除资源或者移动资源时,要选择内容右键修复文件夹中的重定向器,这样才能将引用,资源处理完成。
最后打包项目,运行
最后能看到左上角的打印,前方的带纹理的小方块,终于成功啦,普天同庆,完结撒花。文章来源:https://www.toymoban.com/news/detail-780059.html
如果没有看到上述效果,可以将程序附加到VS的进程进行调试,查看各个路径对不对。文章来源地址https://www.toymoban.com/news/detail-780059.html
到了这里,关于UE5热更新:Pak包的Cook、打包、加载全流程及踩坑经验分享的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!