UE4 Pak 相关知识总结
转载自:https://arcecho.github.io/2017/07/02/UE4-Pak-%E7%9B%B8%E5%85%B3%E7%9F%A5%E8%AF%86%E6%80%BB%E7%BB%93/
近来研究了下UE4中Pak文件的工作流程,并对UE4中的文件系统了解了下。这个过程中,我发现现有的资料讲的比较乱,且有的版本很老。遂在此按照自己理解来简单阐述下UE4中和Pak文件相关的各种知识,如果有什么错误或者不明白的地方请在下方留言或者发送Email:zmy_qwerty@163.com。
需要知道的核心概念
这里根据自己的理解把使用Pak时需要的核心概念提炼出来了。
印证了《Gmae Engine Architechture》中的一些设计
之前看那本书的感触不是特别深,感觉太理论化。在研究UE4的时候,发现UE4实际上的很多设计就是那本书的一些印证。
UE4和绝大多数游戏引擎一样使用了文件系统即资源管理器的方式管理的文件和游戏资源,比如PlatformFile就是可以认为其平台独立层的File system,而使用的Package管理的方式就是对应其资源管理器的概念。
后面记《GEA》为《Gmae Engine Architechture》。
File System
书中列举的主要用途如下:
- 操作文件名及路径
- 对单独的文件进行读写等常规的文件操作
- 扫描文件目录下内容
- 处理异步的文件I/O请求
除了主要用途,文件系统还要可能需要满足资源Packaging,Patching等功能。
我所了解比较好的文件系统例子是暴雪的CASC,我们玩的WOW的更新及修复什么的,都是依靠它实现的。
UE4中的对应的一些概念
File
File在UE4中的概念,实际上和我们使用操作系统时的是基本一致的,用来记录数据信息。
Asset Package
实际上UE4中没有Asset Package这个名词,只有Package,为了区别于Packaging Project的package,这里先声明这样的记法。Asset Package,就是指的在UE4中能够在直接使用的游戏资源,比如material,map,blueprint等,每一个独立的资源的对应一个Package概念。
- 一个Asset Package可能包含多个File,比如cooked之后的usset实际上可能会分离出.uexp .ubulk等文件。
- 多个Asset Package可以称作一个Pack,比如Add Feature Pack就是提供了一系列Asset Packages。
- Asset Package和UOject关系很紧密,直接Load的Asset Package就是一个UObject对象。
- FPackageName就是我们通常在Load Asset时使用的路径形式。更为具体点就是FStringAssetReference。但是注意FStringAssetReference不是一个字串,不要被名字迷惑。
PlatformFile
UE4中使用使用IPlatformFile来抽象出平台无关的文件操作的接口,对应《GEA》中架构的平台独立层中的文件系统。 相对Asset Reference方式来说这是一个低层文件访问的对象。它提供了基本的文件Read,Write,Delete等等操作。比较接近std::fstream的一些操作。针对支持的各个平台都会从IPlatformFile派生出相应的具体实现的类,比如WidowsPlatformFile。除了平台相关的的派生类以外,还有IPlatformFilePak,IPlatformFileModule等,这里只介绍IPlatformFilePak。和上面FPackageName相对应的是FPaths用来处理一些PlatformFile下的文件路径的操作。
Pak PlatformFile
用以管理Pak相关操作的PlatformFile类。提供了使用.pak文件的最为核心的两个操作Mount及Unmount。
引擎提供的相关操作
需要注意的问题
下面是我在学习的时候,总结的一些知识以及容易混淆的概念。
在PIE模式下使用Pak文件的意义不大
我最开始的时候按照自己的理解,认为既然是虚拟文件系统,那么只要我正确挂载路径,那么就可以正常的访问文件数据了。按照样的说法在PIE模式下,这也应该是可行的,那么这样子我在测试Pak相关功能时是不是可以不用那么麻烦的每次都需要Pakcaging Project了呢?虽然这个问题的前半段确实可行,但是后半段的问题仍然无法避免。
- 虽然能够挂载pak文件,并能够加载其中的Asset Package,但是只能是未被Cooked的才可以正常使用,同理Packaging之后的Project也无法使用没有Cooked的Asset Package。
区分Mount和Load的区别
Mount翻译为挂载,而Load一般被翻译为加载。Mount只是告诉了文件系统有哪些文件可以从当前挂载的Pak文件中读到,即提供了虚拟的路径来访问Pak文件包含的文件,而Load一般就是指把文件的内容加载到内存中了,当然加载一个Package不是说的这么简单的流程。另外我看很多示例上可能是没有理解这边文件系统的原理,会在挂载之后直接写一些加载的代码,并没有在此时去使用加载之后的数据。挂载pak文件之后就可以通过常规的方式来访问其中的文件了,并不需要特意的去做一次加载操作,且和使用或不使用异步加载方式没有直接的关系
注意PlatformFile是一个链状结构
如果阅读IPlaformFile相关的代码就会发现IPlatformFile实际上是一个链状的结构。在其Intialize的时候,会要求传一个IPlatformFile的指针,这个将会作为其inner lower PlatformFile存在,在通过一个PlatformFile尝试访问文件目录时,可以通过这个链自顶向下访问每个节点上所挂载的文件。
- 可以这样理解,每个节点都有包含一部分文件信息,整个链上节点的信息综合起来,就是你能够访问的所有文件信息。值得注意的是,这种方式是允许节点间信息冗余的。如果冗余会访问第一个找到的数据。
- 既然是一个链状结构,UE4提供了FPlatformFileManager来方便set/get topmost PlatformFile。
注意一个PakPlatformFile上可以挂载多个Pak文件
网上大多数挂载pak的示例中,都是直接创建新的PakPlatformFile,然后把PlatformFileManager取到的当前的topmost PlatformFile作为其inner lower PlatformFile,然后将这个PakPlatformFile指定为topmost。实际上如果没有特殊要求的话,没有必要去增长这个链,会造成内存的浪费。一个PakPlatformFile可以挂载多个Pak文件,其内部有一个记录FPakFile的List。
注意使用PackageName和Path的区别
一般来说,Path就是接近我们在操作系统中读取文件时的那种路径。而PackageName提供的字串形式,实际上是一种简写的方式。
- FPackageName提供了RegsterMountPoint和UnRegisterMountPoint函数,这个函数的作用实际上就是提供了一种重定向的方式,即你在使用”/Game/xxxx.xxxx”的PackageName时,资源管理器会翻译成文件系统所能识别的Path。注意提过一个Asset Package可能对应多个文件,这个过程可能不是简单的字符串转换。另外还需要注意的是,PIE模式下你使用这个函数的时候,会抛出事件让Content Browser去扫描改动,如果你的注册的文件是在GameDir下的话,会反映到编辑器的Content Browser中。一般来说,这个函数没必要特意去调用,如果你mount的路径在GameContentDir下的话是可以直接使用”/Game/“开头的那种方式来加载Asset Package的。
pak文件中可以存放非Asset Package文件类型
这里的意思是在使用UnrealPak.exe可以放入类似.txt的文件,挂载后可以通过PlatformFile来访问其中的文件内容。
Pak文件大小要求
前面的文章基本上描述了pak文件的使用的基本流程,但是UE4中处理这些流程的复杂程度远不止所提到的这些。比如pak文件在使用的时候还涉及到资源缓存的问题,对应代码中的USE_PAK_CACHE。这个功能,限定了pak文件的最小size。目前4.16.2版本中这个大小被设置为64KB(PAK_CACHE_GURANLULARITY),也就是如果你的pak文件小于64KB,将无法被预缓存。这一点我通过补足pak文件大小和修改源码中的PAK_CACHE_GURANLULARITY数值两个方式进行了验证,结果都符合预期。
UE4.17.1版本中已经把这个问题修正了。
Fixed async loading from pak files < 64k.
制作pak文件的一个小技巧
UE4提供了命令行工具来生成pak文件,我们只需要编写一些脚本,就可以方便按照自己的需求来生成相应的pak文件。这里说的小技巧可以让你把某个路径下的制作成pak文件时保留你想要的部分路径。具体的脚本代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
# This function accepts a short package filepath with out extension.
# And when generating pak file, it will add a dummy file to the reponse file list,
# in this way, the relative path to expected package root path will be saved in pak file.
# For example:
# If the input parameters targetPackagePathRootDir="F:\\AAA\\BBB\\" assetPackage="F:\\AAA\\BBB\\CCC\\*.*",
# then in the output pak file, the file's saved path will start with "\\CCC\\"
def GenerateSplitedPaks(outputPakFileDir, targetPackagePathRootDir, assetPackage):
pakCmdTemplate = '"{}" "{}" {} "{}"'
urealPakToolPath = GetUnrealPakToolPath()
outputPakFileDir = os.path.normpath(outputPakFileDir)
targetPackagePathRootDir = os.path.normpath(targetPackagePathRootDir)
filesToWrite = ""
for associatedFile in assetPackage.associatedFiles:
filesToWrite += ' "{}"'.format(associatedFile)
#Use hash
pakFilename = str(hash(assetPackage.name))
# _, pakFilename = os.path.split(assetPackage.name)
outputPakFilePath = os.path.join(outputPakFileDir, pakFilename + ".pak")
dummyPackagePath = os.path.join(targetPackagePathRootDir, "dummy.uasset")
pakCmd = pakCmdTemplate.format(urealPakToolPath, outputPakFilePath, filesToWrite, dummyPackagePath)
subprocess.call(pakCmd, shell=True)
|
简单来说就是以你想要相对的路径下加一个不存在的文件,上面代码中是dummy.uasset。然后作为命令行输入的时候,工具提示找不到那个文件且不会加入那个文件。但是这个时候,你要保留的路径就留下来了。可能讲的不是很直观,你可以自己尝试几次不同的组合就知道了。
注意这里是简单的技巧示例,真正使用的时候请参照编辑器中的传详细的参数来进行脚本的编写。因为生成pak文件的时候,还需要需要指定合适的patchpaddingalign等数据。详细可以参看工程的ProjectUitls下的python脚本的代码。
挂载前后的路径问题
这个问题非常重要,我也将其放在最后来说。但其实挂载前后的路径确定出乎意料的简单,就是要保证你在制作那些资源时的目录结构和你挂载后的目录结构完全一致,这样就能保证其中资源依赖没有问题。给个简单的例子说明:
图中ToPak路径下的Package我都需要放入到Pak文件中,使用UnrealPak工具,以ToPak为Root生成Pak文件。然后使用此pak文件时,只需要把pak文件在挂载在FPaths::GameContentDir()下,就可以正常使用,包括依赖关系。
- 同一个文件夹目录下的的文件可以分散在不同的Pak文件中,对同一个路径mount不同的Pak文件就可以还原之前的目录结构。
- 同时需要注意的是实际挂载时并不一定需要去指定mount point,但这个的前提是你生成pak文件的时候在response file中已经指定好了各文件的redirection path。这一点是通过阅读程序如何使用自身的paks文件下的pak文件得知的,我做了多次验证后确实如此。且不按照此种方式使用时,静态光源的构建数据可能会出现错误的带状显示效果。
核心的代码示例
工程托管在GitHub上:TestPak, 我只把使用Pak文件最核心的一些代码提炼出来了,并给予一些示例。
目前尚未解决的问题
- 当前我测试通过的的版本是4.15.1和4.16.2,在正确生成pak文件后(正确是指使用unrealPak工具时,相关的命令行参数需要恰当),基本上能够正常运行。如果使用最简单的方式的话可能静态光源的builddata使用错误,出现带状的错误显示效果。然后就是在4.15.3版本下cook之后,只有uasset文件,而缺少uexp等文件,暂时不知道为什么,此种情况下使用时也会出现前面的错误。这个需要等我后面弄明白了再补充。
补充
- 静态光源实际上和生成pak时的response file的格式有关,按照UE4自身的response格式来创建的pak,就可以正确地使用构建的静态光照信息了。
UE4 Pak 相关知识总结的更多相关文章
- 【UE4 C++ 基础知识】<11>资源的同步加载与异步加载
同步加载 同步加载会造成进程阻塞. FObjectFinder / FClassFinder 在构造函数加载 ConstructorHelpers::FObjectFinder Constructor ...
- 【Python五篇慢慢弹(5)】类的继承案例解析,python相关知识延伸
类的继承案例解析,python相关知识延伸 作者:白宁超 2016年10月10日22:36:57 摘要:继<快速上手学python>一文之后,笔者又将python官方文档认真学习下.官方给 ...
- 移动WEB像素相关知识
了解移动web像素的知识,主要是为了切图时心中有数.本文主要围绕一个问题:怎样根据设备厂商提供的屏幕尺寸和物理像素得到我们切图需要的逻辑像素?围绕这个问题以iphone5为例讲解涉及到的web像素相关 ...
- listener监听器的相关知识
从别人的博客上我学习了listener的相关知识现在分享给大家 1.概念: 监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上 ...
- UIViewController相关知识
title: UIViewController 相关知识date: 2015-12-13 11:50categories: IOS tags: UIViewController 小小程序猿我的博客:h ...
- 【转】java NIO 相关知识
原文地址:http://www.iteye.com/magazines/132-Java-NIO Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的 ...
- NSString使用stringWithFormat拼接的相关知识
NSString使用stringWithFormat拼接的相关知识 保留2位小数点 1 2 3 4 //.2代表小数点后面保留2位(2代表保留的数量) NSString *string = [NSSt ...
- iOS网络相关知识总结
iOS网络相关知识总结 1.关于请求NSURLRequest? 我们经常讲的GET/POST/PUT等请求是指我们要向服务器发出的NSMutableURLRequest的类型; 我们可以设置Reque ...
- 电路相关知识--读<<继电器是如何成为CPU的>>
电路相关知识–读<<继电器是如何成为CPU的>> */--> *///--> *///--> 电路相关知识–读<<继电器是如何成为CPU的> ...
随机推荐
- 用JSON将一个字典写入到文件,通过loads()将JSON字符串在转换为本来的类型
通过dumps将字典转换为JSON的字符串,存到磁盘里面
- Django项目:CRM(客户关系管理系统)--57--47PerfectCRM实现CRM客户报名流程02
图片另存为 16*16 名字修改为 bpm_logo.jpg /*! *bootstrap.js * * Bootstrap v3.3.7 (http://getbootstrap.co ...
- Shell 工具之 sed
sed编辑器称为流编辑器(stream editor).可以根据输入命令行的命令或者存储在命令文本文件中的命令处理数据.每次从输入读取一行数据,将该数据与所提供的编辑器命令进行匹配,根据命令修改数据流 ...
- idea2018.1.5永久破解过程
可以根据官网推荐注册idea:http://idea.lanyus.com/ 步骤如下:1 下载破解(crack) jar 包 链接:https://pan.baidu.com/s/1-COPHVJi ...
- Redis 持久化之RDB和AOF详解
一.RDB 详解 RDB 是 Redis 默认的持久化方案.在指定的时间间隔内,执行指定次数的写操作,则会将内存中的数据写入到磁盘中.即在指定目录下生成一个dump.rdb文件.Redis 重启会通过 ...
- 9.2专项测试-Android性能测试黑盒分析-1
1. 专项测试 业务测试:面向新需求 回归测试:面向已交付需求 专项测试:面向非功能需求的各类质量唯独特征 表现 用户维度 技术维度 崩溃 crash,弱网 检测崩溃1.某个页面,因为研发处理不合适, ...
- TZ_05_Spring_Proxy基于接口的动态代理和基于类的动态代理
代理:为了增强方法在不添加代码的情况下 1.Proxy基于接口的动态代理 /** * 模拟一个消费者 * @author Administrator * */ public class Client ...
- TZ_01MyBatis_SqlMapConfig.xml
1.sqlMapConfig的配置 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE conf ...
- 20190716-T1-礼物
呵呵,我暴力WA了 这个题充分考验了大家对数学的理解(麦蒙大多在胡诌) 但是确实如此啊. 看数据范围想状压.(我额嗯嗯想到暴力?) 然后设出一个可爱的$dp$式(主语当然不是我,是出题人大佬) $f_ ...
- 对BOM的总结
参考:JavaScript半知半解 TG著 BOM对象 Window对象是客户端JavaScript程序的全局对象. Window对象使得JavaScript与浏览器进行交互. 所有的JavaScri ...