PakDemo项目旨在展示由虚幻引擎项目创建的一个独立应用程序,该应用可以从本地文件系统或在单独项目中烘焙的可寻址路径加载.pak文件。本教程将介绍用于生成Pak文件的PakTestContent项目和在运行时加载Pak文件的PakDemo项目。
概述
除了应用程序的主可执行文件之外,虚幻引擎(UE)还能以.pak文件的形式交付资产。为此,我们需要将资产整理成文件块,即烘焙处理可以识别的资产文件组。本示例将展示如何在虚幻引擎编辑器中将资产整理成文件块。最终产生一个示例项目,该项目将生成.pak文件,你可以利用补丁系统交付这些文件,利用C++蓝图函数库和蓝图加载内容。
Pak文件依赖于对函数的软引用,你的主要虚幻引擎应用程序将通过这种方式与任意Pak加载内容进行沟通。参阅官方文档了解对象引用在虚幻引擎中的工作方式。
PakMountPoc.zip示例项目文件下载:
https://epicgames.box.com/s/hecu7ew61l3yu9smc5ll44vp8xdbdtgz
包含两个项目和一个Windows可执行文件示例版本。注:由于缺少一组可执行文件,此文件已更新。请下载最新的项目文件。
PakDemo项目旨在展示由虚幻引擎项目创建的一个独立应用程序,该应用可以从本地文件系统或可寻址路径加载任意.pak文件。
本文将介绍可生成Pak文件的PakTestContent项目和在运行时加载Pak文件的PakDemo项目,以作参考。
演示
解压后的“Build”文件夹中有一个针对PakDemo项目的Windows可执行文件,用于快速运行应用程序并在本地进行测试。
该应用程序有一个”Pak Path(Pak路径)”输入框用于输入文件路径。输入PakDemo.pak文件的有效路径(如图)。
点击“加载PAK(Load PAK)”按钮导入Pak文件的内容并打开。在这个示例中,有一个蓝图生成了旋转的UE标志。屏幕左上角会打印出一些信息。
“清除PAK(Clear Pak)”按钮可以从运行时应用程序中卸载Pak文件内容。
版本和平台匹配规则
Pak文件很像Uassets,兼容特定的虚幻引擎版本。使用虚幻引擎5.1创建的Pak文件适用于虚幻引擎5.1版本,但不适用于之前的版本或更高版本。在对项目进行版本控制时,最好为应用程序使用强大的版本控制措施。
虚幻引擎会以其内部使用的特定格式存储内容资产,例如以PNG格式存储纹理数据,以WAV格式存储音频。但是针对不同平台,这些内容需要转换成不同格式,可能是因为平台使用专有格式,不支持虚幻引擎用来存储资产的格式,又或者存在其他更有利于节省内存或提高性能的格式。将内容从内部格式转换为平台特定格式的过程叫烘焙。参阅官方文档了解更多有关烘焙的内容。
Pak文件针对特定平台进行烘焙,在Windows上生成的Pak文件与安卓系统不兼容。因为文件内容是为目标平台和着色器图形语言烘焙的。在某些边缘情况下,缓存文件和与平台无关的类似内容可以跨平台共享,但原则上需要为每个平台打包。
创建Pak文件
PakTestContent项目展示了如何生成在应用程序中使用的Pak文件。为了方便管理项目和应用程序使用的ID,强烈建使用文件块ID策略。Pak文件非常强大,需要主动管理。
Pak生成流程概览
-
-
组织项目中的Pak内容以满足应用程序的需求
-
为Pak输出定义数据
-
配置项目以生成Pak文件
-
打包项目以生成Pak文件
-
检查Pak文件内容(可选项)
-
参阅官方文档了解准备资产以进行分块和打包的具体流程。示例项目详情如下。
1.组织Pak内容
在虚幻引擎编辑器的内容浏览器中有一个专门用来保存每个Pak文件的文件夹结构,即pakTest文件夹。
pakTest中的子文件夹示例:
-
Core
-
蓝图示例
-
-
Mdl
-
静态网格体
-
-
Shd
-
各种材质和一个实例,用来展示Pak文件中可能存在的任意内容
-
2.数据资产(主要资产标签)
在pakTest文件夹中有一个用于定义Pak输出的数据资产。
要创建这种类型数据资产,只需在内容浏览器中点击鼠标右键,选择“杂项(Miscellaneous)”->“数据资产(Data Asset)”
选择“主要资产标签(PrimaryAssetLabel)”类
根据下列原则配置数据资产:
-
文件块ID
-
每个打包文件唯一的整数值
-
为定义的每个文件块ID整数值(例如:100)生成一个Pak文件。每个ID都应该特定于基于项目结构的期望结果。建议跟踪并生成作为通用唯一标识符(UUID)或类似标识符的文件块ID,以免在使用大规模应用程序时发生冲突。整型ID的值越大,烘焙时间越长,建议将这个值维持在10,000以下。
-
-
-
递归应用
-
已启用
-
这意味着pakTest内容文件夹中的子目录将包含在Pak文件输出中。默认情况下,pakTest文件夹根目录中的项目也将包含在内。
-
-
-
烘焙规则
-
始终烘焙
-
这可以确保默认在打包期间始终烘焙该内容。这里有一些选项可供探索,在这个例子中,我们将始终烘焙内容,以确保生成内容。
-
-
参见官方文档了解有关文件块设置的更多内容。
示例项目提供的数据资产设置示例
3.项目设置
参见官方文档了解有关项目设置的更多内容。
需要为项目配置的关键设置
打包设置
-
-
已启用
-
将项目的资产打包为单个文件或单个包。如果启用,所有资产都将被放入单个.pak文件,而非复制所有单个文件。如果项目使用大量资产文件,那么使用Pak文件可以让发布变得更加简单,因为它可以减少需要传输的文件数量。此选项默认禁用。
-
-
生成文件块
-
已启用
-
是否生成可用于流送安装的.pak文件块
-
-
-
资产管理器设置
主要资产标签 - 仅编辑器
-
-
已禁用
-
将没有主要资产标签的内容生成为Pak0,这些内容并不仅供编辑器使用。Pak0总包含没有用数据资产定义的内容。
-
-
4.安卓OBB过滤器定义
安卓使用OBB文件将应用程序分成多个部分,类似于我们的Pak文件。要注意的是,必须为项目定义defaultenengine.ini,从而将每个Pak文件块从OBB打包中过滤出来。这将从OBB中排除名称中包含所提供字符串任意部分的Pak文件。然后,Pak文件就可以与APK和OBB方法分开处理。
[/Script/AndroidRuntimeSettings.AndroidRuntimeSettings]
+ObbFilters="-*pakchunk1*"
+ObbFilters="-*pakchunk2*"
+ObbFilters="-*pakchunk3*"
+ObbFilters="-*pakchunk4*"
+ObbFilters="-*pakchunk5*"
+ObbFilters="-*pakchunk6*"
+ObbFilters="-*pakchunk7*"
+ObbFilters="-*pakchunk8*"
+ObbFilters="-*pakchunk9*"
5.打包项目以烘焙Pak文件
使用常规流程打包项目。记住,Pak文件将根据特定平台生成,因为它们是按照目标平台的规格烘焙的。
在弹出的窗口中为要构建的打包版本选择一个文件夹。我选择了示例文件夹根目录中的Build文件夹。注意,我没有选择PakDemo文件夹,因为PakDemo是和这个演示项目一起分享的预构建项目。创建适合项目的结构。
等待打包完成
打包完成后,虚幻引擎编辑器会通知你
现在,Build目录中有一个Windows文件夹,其中包含构建过程输出的文件
我们要关注的重点是构建过程输出的Pak文件,这些文件位于PakTeskContent\Content\Paks。
虚幻引擎编辑器将按下列命名格式生成Pak文件:
-
pakchunk[文件块ID]-[平台]
-
例如上图所示的pakchunk111-Windows.pak。我们在数据资产中选择的文件块ID是111。
建议使用文件块ID的定义规则跟踪这些Pak文件,以便将来在其他应用程序中使用Pak文件。Pak文件可以重命名。例如,示例根目录中的PakDemo.pak文件就已经重新命名。
6.检查Pak文件内容(可选项)
检查UnrealPak.exe,查看Pak文件内容,包括路径
Unrealpak.exe pak文件路径 -列表
有关Pak生成的常见问题解答
Pak文件块ID是否必须是唯一的?
是的,Pak文件块ID在项目中必须是唯一的。文件块ID UUID生成器可用于实现更高级的ID分配,出于性能原因,数值应保持在10,000以下。
如果我只想添加内容,是否需要烘焙和打包我的应用?
这要视具体管线而定,只要主应用程序中的逻辑没有改变。Pak文件依赖软引用,只要原始应用程序没有更改,就可以将内容与主应用程序逻辑分开。
是否可以重新命名输出的Pak文件?
可以重新命名Pak文件。一种常见做法是创建一个实用程序来管理项目中的Pak文件块ID,设置有意义的名称,并使用数据表或其他机制进行跟踪。
是否可以对Pak文件进行签名和加密?
可以。在发布的产品中分发时,Pak文件可以签名或加密,通常是为了防止数据提取或篡改。要激活、停用或调整项目上的密码设置,可访问项目设置(Project Settings)菜单,找到加密(Crypto)部分。
是否有针对虚幻引擎Pak文件的打补丁方法?
有,参见应用程序打补丁流程相关文档。
在运行时应用程序中加载Pak文件
PakDemo项目是一个C++项目示例,旨在介绍Pak加载器功能。本示例将展示如何直接在运行时应用程序中加载Pak文件。
1.打开PakDemo项目
PakDemo项目是在虚幻引擎5.1版本中创建的,但相关概念适用于自4.27以来的版本。PakDemo项目是一个C++项目,加载时可能会弹出如下提示:从源代码重新构建模块。点击“是”。
2.项目概览
-
内容文件夹
-
BP_PakLoader是一个蓝图示例,用于展示如何在运行时应用程序中加载外部Pak文件。
-
PakTest(关卡)是应用程序的主关卡。
-
UI_PakTest是一个控件蓝图,包含与用户界面元素相关的内容和逻辑。BP_PakLoader函数由它实现。
-
-
C++类/PakDemo
-
BFL_Pak(蓝图函数库)是用虚幻引擎API实现Pak处理蓝图功能的C++代码。
-
-
-
PakDemoGameModeBase是一个未使用的游戏模式实现(供参考),由虚幻引擎在创建C++项目时自动生成。
-
-
PakTest关卡内容
-
-
Lighting文件夹中包含用于照亮关卡的各种光源Actor。
-
BP_PakLoader是用于在关卡中实现蓝图逻辑的关卡Actor。
-
摄像机Actor是确保玩家拥有预期视角的摄像机。
-
目标点是设置Pak内容生成位置的Actor。
-
-
关卡蓝图
本文旨在介绍项目内容和C++编程语言的细节。作为快速参考,BFL_Pak.h头文件将显示在虚幻引擎中注册的公开UFUNCTIONS。这与上面截图中列出的蓝图函数相符。
这些函数是在BFL_Pak.cpp文件中实现的。在CPP文件中搜索函数名就可以查看每个函数的工作方式。例如下图中的MountAndRegisterPak函数。
参阅官方文档了解有关虚幻引擎C++编程的更多内容。
4.使用蓝图安装Pak内容
在玩家输入Pak文件的文件路径字符串后,点击“加载Pak(Load Pak)”按钮触发“加载Pak(LoadPak)”事件(红色节点)。
蓝图逻辑如下:
-
在视图中向用户打印一条消息,显示“Pak文件:相应路径”。
为Pak文件路径(PakFilePath)设置一个变量,以便在蓝图范围内使用。
检查是否已加载Pak文件,如已加载,则提醒用户。
-
-
这是在设计这个应用程序时决定的,不是虚幻引擎的限制,因为预计只有一个Pak,所以在跟丢Pak引用前进行验证。
-
由于仅有的Actor实例是在关卡中生成Pak内容的生成器,你可以在关卡中放置多个实例,以更动态的方式与它们进行交互。
-
-
如果当前未加载任何Pak,则调用C++函数“安装和注册Pak(Mount and Register Pak)”,使用PakFilePath作为输入变量。
-
将这项安装操作的结果作为消息打印出来,将操作成功的信息输入分支进行验证。
5.生成Pak内容
-
安装成功后,使用C++函数“获取Pak内容(Get Pak Content)”将Pak内容作为一个数组输入For Each循环。
-
针对每个数组元素,使用子字符串检索查找包含关键字“BP_UeLogo”的内容。
-
找到目标内容后,它会将“预览Pak路径(Preview Pak Path)”变量设为内容的路径,打断循环。
-
-
循环中断或自然完成时,通过分支验证“Preview Pak Path”是否为空。
然后,使用“类型转换至Actor(Cast To Actor)”节点确保内容是生成器函数要求的Actor类。
-
使用“生成Actor(SpawnActor)”蓝图函数在目标位置生成内容。
-
目标是一个公开变量,引用了关卡中的一个Actor。
-
这一引用为“SpawnActor”提供了一个变换位置,用于放置生成的内容。
-
-
玩家收到Pak已加载的信息。
6.销毁Actor和卸载Pak
-
点击用户界面中的“清除Pak(ClearPak)”按钮将调用蓝图中的“清除Pak(ClearPak)”事件。
-
首先,我们要验证Pak资产是否有效,如果无效,玩家将在视图中看到如下消息:“没有可清除的内容!”。
-
如果Pak资产有效:
-
-
玩家将在视图中看到如下消息:“清除资产:Pak资产显示名称”。
-
函数返回到“增加字符串(Append String)”节点中的Pak资产显示名称。
-
Pak资产引用的Actor已销毁。
-
使用C++函数“卸载Pak文件(Unmount Pak File)”卸载Pak文件。
-
玩家将在视图中看到如下消息:“Pak已卸载:Pak路径”。
-
7.管线流程
这个项目使用简单的用户输入文本字符串来标识要加载的Pak。在实际项目中,需要定义系统如何查找、解析和利用Pak内容的逻辑。这个逻辑对每个应用程序来说各不相同。记住,加载完Pak之后,可以使用Actor标签、标记和其他常见的虚幻引擎对象标识符来确定如何处理每个Pak文件的内容。优秀的命名方式和组织结构有助于管理变更和降低复杂性。在游戏中,一种常见的做法事为关键游戏元素创建一组特定的类,以确保这些类有序且有明确定义。如果你在一个大型生态系统中工作,可以考虑根据项目的目标创建特定的Actor类。虚幻引擎在这方面非常灵活和开放。
示例项目的逻辑是在先在虚幻引擎中安装和注册Pak,然后使用“包含(Contains)”函数对Pak文件的内容进行解析,明确查找子字符串“BP_UeLogo”。了解如何在关卡中生成实例和通过引用来处理Actor等游戏概念有助于我们根据不同的管线和应用需求设计正确的架构。
8.用户界面元素逻辑
在这个例子中,用户界面和视口的绑定通过蓝图Actor来处理。这不是唯一的方法,你可以根据应用程序的需求选择其他方法。在“开始运行事件(Event BeginPlay)”上为UIPakTest控件生成一个用户界面,并将它添加到了所属玩家的视口上。
UI_PakTest用户控件资产包含界面的布局和逻辑。布局和控件是在用户控件编辑器的设计器模式下创建的。布局很简单,画布面板上有一个标签、两个按钮和一个供用户输入路径的输入框。
按钮和其他事件的逻辑是在用户控件编辑器的图表模式中创建的。
逻辑如下:
在“构造事件(Event Construct)”上定义控件的父项并将它设为变量“Parent”,保存这个变量以备后用。
使用“获取类的Actor(Get Actor Of Class)”函数找到BPPakLoader蓝图引用,它将返回对象引用,将返回值设为变量“BpRef”。
这是直接引用或硬引用。它将用户界面绑定到关卡中的蓝图上,这样就可以直接将事件传输到蓝图中。这可以与蓝图接口解耦。更多信息请参阅官方文档。
点击“重置(Reset)”按钮将调用BpRef蓝图中的ClearPak事件,参见本文的“销毁Actor和卸载Pak”部分。
点击“加载(Load)”按钮将从输入框“Path(路径)”中获取文件路径文本,并将其转换为“文本(Text)”,传输给BpRef蓝图中的LoadPak事件,参见本文的“使用蓝图安装Pak内容”部分。
总结:
整个逻辑相对来说比较简单,本文的主要目的是展示加载和销毁Pak文件的基本实现方法。下面列出了官方文档和其他资源。这个示例项目不提供任何隐含支持,希望这个项目能激励大家进一步了解和挖掘自定义开发C++Blueprint函数库的潜能。