高级游戏资源打包技术详解

一个大型的商业游戏包含很多资源,如图像、声音、文本、脚本和其他各种类型的数据,为游戏提供一 个完整和高效的资源管理系统(包括SDK与编辑器)是游戏引擎开发商必须完成的工作。我们在Numen Game Engine 2.0中实现了一个底层 资源打包接口,在接口的基础上实现了纹理编辑器、模型编辑器、单位编辑器和建筑物编辑器等工具,整个系统采用32位的ID来标识资源,具有一级分类的能 力,支持资源包的各种常用操作。
    但是在Numen Game Engine 3.0中,我们为了实现一个完美的资源打包系统,完全放弃了 以前的所有接口,采用更多新的技术,最终构建了一个功能强大,性能高效的资源管理接口,这篇文章中,我们将从资源编辑器的特性简介、关键技术和资源编辑器 功能三个方面,介绍构建一个商业级资源打包系统需要掌握的关键技术和实现细节。

一、特性简介
    Numen Pack Editor 2.0支持以下主要功能特性:
1、创建、打开和关闭资源包。
2、压缩资源包。消除数据添加、修改和删除带来的数据碎片,扩展索引表容量,重设资源包密钥。
3、合并资源包。将外部的资源包整合进当前打开的资源包。
4、释放资源文件。将资源包内的任意文件释放到磁盘。
5、创建文件夹。
6、(批量)添加文件。可以指定文件的独立密钥与压缩选项。
7、从文件夹添加数据(包括文件夹与文件)。可以指定文件的独立密钥与压缩选项。
8、(批量)更新文件。可以选择按列表顺序与按文件名两种方式进行数据更新。
9、(批量)复制、剪切、粘贴、删除和重命名单元。
10、查看和修改资源属性。
     资源编辑器的功能实现主要依托于底层SDK提供的接口,同时,细致周到的用户界面设计和大量方便的组合功能,是编辑器使用层次提高的关键。 Numen Pack Editor 2.0在设计中,仅界面的设计就花费了大量时间,期间主要是对各种细节进行反复测试和修改。

二、关键技术
     总的来说,为了确保资源管理系统的高效,需要实现散列表、目录树、存储空间管理、加密/解密和数据压缩五个主要的接口,但是要实现完整的资源系统,还需要 其他大量的基础函数,Numen Game Engine 2.0升级到3.0后,只需要实现前三项技术即可完成整个系统的构建。
    (一)散列表
     众所周知,散列表的出现就是为了解决字符串或非简单数据类型(如32位整数)的索引。一般来说,首先将需要索引的数据进行计算,产生一个32位的索引值, 好的算法可以使这个索引值分布比较平均,减少产生冲突的可能性;插入操作时,用这个索引值与索引表大小进行模运算,获得它在索引表的位置,如果计算出的位 置为空,则直接插入元素,否则将进行冲突处理;查询操作时,先在计算出的第一个索引表位置进行查找,发现元素则返回,否则继续进行查找;删除操作同查询操 作,只是找到元素后会给元素打一个删除标记。
    我们有很多的散列算法可用,这里推荐暴雪在MPQ中开发的索引算法,其实该算法最大的好处在 于不在散列表中存储要索引的数据,这样在比较操作时就不需要进行长数据的比较,也可以节约存储空间(后面会说到这个只对索引表有直接益处),举例来说:我 们要插入一个字符串”e/world/texture/char/tank.png”(长度29字符),如果我们在索引表中存储完整的字符串,最理想的情 况下我们也需要比较29个字符长度的数据,如果发生冲突,还将进行更多的比较。
    在MPQ中引入了两个额外的32位索引值来提供比较,这样 在任何情况下都不会进行长数据的比较,如上面那个字符串,我们得到索引值dwHash,计算两个参考值dwCheck1和dwCheck2,这样每次元素 比较最多只需进行3次32位整数值的数据比较。值得一提的是,这个算法也并不是万能的,在实际应用中,如果我们不需要大量长索引,那么普通算法的劣势将不 明显,同时还将节约dwCheck1和dwCheck2的计算时间。
    另一个关键就是如何在索引表中放置冲突元素,一种方法是用链,另一种 方法使用顺序,在MPQ中使用的是顺序存储,这样做的好处是实现简单,但是在最坏情况下,查找一个元素可能将遍历一次索引表,但是在正常游戏中,一般不会 查询错误的单元,所以这样的情况很少出现。顺便提一下,在将冲突数据进行链的处理时,由于索引表需要存储在文件中,所以这里链需要改造成索引链(如下一个 位置是15),而不是采用内存链表。
    (二)目录树
    在资源包中我们对每个单元的全路径进行索引,如e/texture/ tank.png,那么在资源管理器中我们不可能通过遍历索引表来构建整个目录树(在1万个以上项目时速度将会明显变慢),而且我们的索引表也不存储任何 名称信息。这就需要我们实现一个类似文件树的接口,它将保存所有的目录信息。
    在Numen Game Engine 3.0中,我们用链表实现了目录树,目录树的单元数据结构如下:
struct FILETREEITEM
{
FILETREEITEM* pPrevItem; // 前一个单元
FILETREEITEM* pNextItem; // 后一个单元
FILETREEITEM* pChildItem; // 子单元
FILETREEITEM* pParentItem; // 父单元
DWORD dwHashIndex; // 索引序号
int nNameLength; // 名称长度
WCHAR strItemName[1]; // 单元名称,不包含路径.(可变长度)
};
     采用“上下左右”四个单元指针的目的是为了提高插入的效率和使单元访问更容易,Numen引擎中用了一个小技巧,就是每个单元的第一个子单元(包括根单 元),它的pPrevItem指向的是本级最后一个单元,这样做是为了使插入操作能进行后序插入,普通情况下,要实现这个目的,需要提供一个额外的 pLastItem指针。
    设计目录树时的难点在于指针的正确处理,如果说单向链表很简单,双向链表并不难,那第一次设计这种属性链表时一定不会一帆风顺的。另外还需要注意一些操作影响的不仅仅是一个单元,还将影响所有的子单元。
    使用可变长度的单元名称将节约大量内存空间。
    为了使各种操作有更多的可定制性,我们还需要在对目录树的各种操作中加入回调函数,如下所示:
// 文件操作参数
#define FILETREEOP_RENAMEITEM 0x00000001 // 重命名单元
#define FILETREEOP_COPYITEM 0x00000002 // 复制单元
#define FILETREEOP_MOVEITEM 0x00000004 // 移动单元
#define FILETREEOP_REMOVEITEM 0x00000008 // 删除单元
#define FILETREEOP_OVERWRITE 0x00000010 // 覆盖操作
#define FILETREEOP_INTERNALCREATE 0x00000020 // 内部创建

// 文件树操作回调函数
// 参数: -strSrcName 源单元名称
//       -strDstName 目标单元名称
//       -dwFlags 文件操作参数,参考FILETREEOP宏定义.
//       -lpUserData 用户自定义数据
// 返回: 0: 执行单元操作
//    其他: 放弃单元操作
typedef LRESULT (CALLBACK *LPCBFILETREEOP)( LPCWSTR strSrcName, LPCWSTR strDstName, DWORD dwFlags, LPVOID lpUserData );
    有了这些回调函数,这样我们就可以非常灵活的实现我们的资源管理器了。
    最后值得一提的是,目录树也是实现采用检验值方式的索引表进行重构的基础,因为没有存储索引信息的索引表,是不可能改变自身大小的,必须通过外部记录的索引信息,将索引表扩容后,进行索引重建。
    (三)存储空间管理
    对操作系统比较熟悉的人都知道,文件系统在长时间使用后会产生大量数据碎片,资源管理器也将面临这个问题。在Numen Game Engine 2.0中,我们只是简单的把任何新增的数据加入到资源包的结尾处,可以定期通过压缩资源包来消除空间浪费。
     在Numen Game Engine 3.0中,我们实现了一个空间管理类,这样任何空间的申请和释放都通过这个类来管理,这个类建立了32个单向链 表,每个链表记录一定容量的空闲空间信息,如我们先后释放了4096,89,12589大小的存储空间,然后我们添加大小为70,4000,10000大 小的数据,我们将可以申请到重复利用的空间,而不需要增加资源包的大小,同时未用完的空间将被加入到链表中等待分配。
    正是由于存储空间管理器的引入,大大降低了数据的删除和修改操作对资源包大小产生的影响,在配合资源压缩功能,完全不必担心数据碎片带来的空间浪费。
    (四)数据加密/解密
    这个方面的内容可参考的教程和代码较多,算法的强度和效率也各有不同,所以开发商可以根据引擎定位和项目需求进行具体定制。
    资源包中的加密分为文件头加密、索引表加密、目录树加密、数据块信息加密和数据加密,这些加密方式为资源包提供了完整和灵活的保护方案,用户可以根据数据的重要程度和读取效率需求自由定制加密方案。
    (五)数据压缩
     通过提供各种压缩算法,如Huffman,RLE,ZIP,LZW等,为引擎数据提供压缩服务,目前Numen Game Engine 3.0的资源包 支持Huffman,RLE和LZW三种压缩算法选择,用户可以自己选择使用他们中的一种或多种对数据进行压缩。

三、资源编辑器功能
    这里将对Numen Pack Editor 2.0中的部分功能进行图文说明:
    (一)压缩数据包
    这里为用户提供了改变索引表大小和资源包加密的机会,资源包索引将会对这个资源包进行重构,花费的时间根据资源包的数据大小,加密强度和压缩等级而定。
    (二)合并资源包
    合并资源包是为了团队配合制作资源而产生,比如美工进行分工后,可以自行制作自己的图形资源,并保存在自己的文件夹中,当资源需要进行整合时,可以统一汇总,而且每个资源包可以保持自己独立的密钥,使团队合作的层次得以体现。
    (三)添加文件
    如果不能批量添加文件到指定文件夹,那添加文件将会变成一件浪费人力的工作,同样我们也需要为一批添加的文件同时指定加密和压缩方案。
    (四)添加文件夹的文件
    这是一个十分重要的功能,当用户有一大批已经制作好的资源需要进行添加时,我们不需要手工建立所有的文件夹,只需要点击几次鼠标,便可将所有资源一次性加入到资源包中。
    (五)更新文件
    如果一个资源编辑器不具备强大和灵活的更新能力,那就不叫资源编辑器了。用户首先选择需要更新的文件,然后选择按序号或者命名更新,就可以一次性完成一批数据的更新操作,而且编辑器提供了更新确认对话框,所以用户不必担心误操作覆盖掉重要的数据。
    (六)文件属性
     如果文件加入资源包后,不能随时查看和编辑它的属性,这将是不可想象的灾难,因为我们根本无法预知未来的需求变化,比如添加时我们希望数据能快速的读取, 所以没有给它加入加密和压缩,但是游戏发布后,我们希望保护我们的资源,这是利用文件属性对话框就可以轻易的实现这个目的。
    四、结束语
    Numen Pack Editor 2.0 已经比较完善的支持了资源包管理系统应具备的各种功能,而且在用户接口设计上非常注重细节,为用户进行资源处理提供了高效的手段。但是一切技术都是不断发 展和完善的,现在的编辑器也需要在更多的项目实践中不断完善和发展,关于Numen Game Engine和Numen Software的更多信息请 随时访问www.numenstudio.com进行查询。

由于GameRes发带图片的帖子不方便,要下载程序和图文教程的请访问www.numenstudio.com。
下载地址:http://www.numenstudio.com/media/PackEditor2.0.rar


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值