首先,需要有一个打包好的PAK文件,可以参考我上一篇博客:UnrealPak打包
Editor下只能用未COOK的资源,打包后只能用COOK了的资源,否则加载不了
本例采用已加密的PAK文件,包含解密过程
简单讲解下几个知识点:
挂载(Mount):简单理解为保存资源的路径,在加载时就可以通过这个路径找到资源
加载(Load):将资源加载到内存,必须先挂载才能加载
挂载点(MountPoint):即将文件虚拟的挂载到某个路径上,在UE4中即为/Game可以直接读取到的位置
IPlatformFile:UE4的文件系统,创建时必须提供上一个IPlatformFile,呈链状
FPakPlatformFile:继承自IPlatformFile,用来挂载PAK
FPlatformFileManager:管理IPlatformFile的类,SetPlatformFile是设置最顶层的IPlatformFile
TSharedPtr<>:UE4封装的智能指针,这是是用于c++原生类的指针
直接上代码,在注释里进行详解
Build.cs添加PakFile模块
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" , "PakFile"});
在自己的GameInstance.h添加如下代码:
//GameInstance.h
public:
UYourGameInstance(const class FObjectInitializer&);
virtual void Init(); //游戏启动时运行
virtual void Shutdown(); //游戏关闭时运行
private:
IPlatformFile* PlatformFile; //初始文件系统
TSharedPtr<FPakPlatformFile> PakPlatformFile; //Pak文件系统
在自己的GameInstance.cpp添加如下代码:
UYourGameInstance::UYourGameInstance(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer) {
//绑定PAK解密函数,如未加密可注释
FCoreDelegates::GetPakEncryptionKeyDelegate().BindUObject(this, &UYourGameInstance::InitEncrypt);
//保存初始文件系统指针
PlatformFile = &FPlatformFileManager::Get().GetPlatformFile();
}
void UYourGameInstance::Init() {
Super::Init();
//新建PAK文件系统指针
PakPlatformFile = MakeShareable(new FPakPlatformFile());
//在PAK文件系统设置Parent文件系统,
PakPlatformFile->Initialize(PlatformFile, TEXT(""));
//将PakPlatformFile设置为最顶层,即首先查找的PakPlatformFile内的文件
//再查找PakPlatformFile的Parent的文件,以此类推
FPlatformFileManager::Get().SetPlatformFile(*PakPlatformFile);
//这里的文件系统结构,PlatformFile是初始的,所以只有两层
//PakPlatformFile --> PlatformFile
}
void UYourGameInstance::Shutdown() {
//恢复初始的文件结构
if (PlatformFile) {
FPlatformFileManager::Get().SetPlatformFile(*PlatformFile);
}
//这里的文件系统结构,PakPlatformFile 从链表中移除,里面挂载的文件将找不到
//PlatformFile
}
//挂载函数
//PakName : PAK包的路径,相对路径或绝对路径皆可
//MountPoint : 挂载点的位置,编辑器下相对或者绝对路径皆可,打包后切记一定使用相对路径,用于兼容各种文件加载
void UYourGameInstance::MountPakWithMountPoint(FString PakName, FString MountPoint) {
if (FPaths::FileExists(PakName) && PakPlatformFile.IsValid()) {
//获得已挂载的PAK文件,避免重复挂载
TArray<FString> ExistPaks;
PakPlatformFile->GetMountedPakFilenames(ExistPaks);
if (ExistPaks.Find(PakName) >= 0) {
return;
}
//将PAK挂载到MountPoint
if (PakPlatformFile->Mount(*PakName, 1, *MountPoint)) {
GEngine->AddOnScreenDebugMessage(INDEX_NONE, 60.f, FColor::Red, *FString::Printf(TEXT("Mount OK %s %s"), *PakName, *MountPoint));
}
else {
GEngine->AddOnScreenDebugMessage(INDEX_NONE, 60.f, FColor::Red, *FString::Printf(TEXT("Mount File %s %s"), *PakName, *MountPoint));
}
//到这里其实已经完成了,后续就可以正常使用PAK包里的资源,用于项目中挂载和加载最好分开,一定要理解这个概念
//但是第一次写的同学可能不太明白,下面写一些是测试代码用于加载和调试
{
//获得刚才挂载的PAK内所有文件路径
TArray<FString> FileList;
Pak->FindFilesAtPath(FileList, *Pak->GetMountPoint(), true, false, true);
//从PAK包内静态加载StaticMesh
for (FString& FileName : FileList) {
if (FileName.EndsWith(TEXT(".uasset"))) {
FString NewPath = FileName;
NewPath.RemoveFromEnd(TEXT(".uasset"));
FString ShortName = FPackageName::GetShortName(NewPath);
int32 Pos = NewPath.Find("/Content/");
NewPath = NewPath.RightChop(Pos + 8);
NewPath = "/Game" + NewPath + "." + ShortName;
//比如资源路径为 ../../../Content/Test/YourMesh.uasset,
//挂载点为 FPaths::ProjectContentDir()
//转换为 /Game/Test/YourMesh.YourMesh 进行加载
UObject* LoadObj = StaticLoadObject(UObject::StaticClass(), NULL, *NewPath);
if (LoadObj) {
UStaticMesh* Mesh = Cast<UStaticMesh>(LoadObj);
if (Mesh) {
if (GEngine && GEngine->GameViewport && GEngine->GameViewport->GetWorld()) {
UWorld* CurWorld = GEngine->GameViewport->GetWorld();
AStaticMeshActor* MeshActor = CurWorld->SpawnActor<AStaticMeshActor>(AStaticMeshActor::StaticClass(), FVector(0, 0, 0), FRotator(0, 0, 0));
if (MeshActor && MeshActor->GetStaticMeshComponent()) {
//设置这个StaticMesh为可移动的
MeshActor->GetStaticMeshComponent()->SetMobility(EComponentMobility::Movable);
MeshActor->GetStaticMeshComponent()->SetStaticMesh(RealMesh);
GEngine->AddOnScreenDebugMessage(INDEX_NONE, 60.f, FColor::White, MeshActor->GetName());
}
}
}
}
}
}
}
}
}
//解密函数,没啥好说的,就是用Base64转换下
void UYourGameInstance::InitEncrypt(uint8* Key) {
FString KeyStr = TEXT("kDIta832HPHzaP+M8bDKrTGxnlU8QGYcEAWN5AAiLYI=");
TArray<uint8> KeyBase64Ary;
FBase64::Decode(KeyStr, KeyBase64Ary);
char* KeyU8 = TCHAR_TO_UTF8(*KeyStr);
FMemory::Memcpy(Key, KeyBase64Ary.GetData(), FAES::FAESKey::KeySize);
}
关于各资源间的引用关系各位可以查查其他文档,我没有细测
如果是用UE4播放PAK中的多媒体资源,如 .mp4,
有些额外的内容需要注意,非常恶心,
MediaPlayer自身会对路径进行一个转换(恶心恶心),可能会导致从PakPlatformFile中查不到资源,此类资源打包后一定要用相对路径,MountPoint和播放时填写的路径要一致,这块是直接字符串比较的,
比如你使用的MountPoint是相对路径,那么播放时填的路径也要相对路径