我们要做一款打飞机游戏,里面有飞机图片、背景图片、飞机音效、碰撞音效等等非常多的素材。如果将这些资源都放置在一个目录下,将会变得非常混乱。如果按照素材内容来划分目录,程序读取的效率就不高,所以我们需要将这些素材打包在一个资源包内,然后将每个素材都放置在一个虚拟目录内。

因此,今天我们就来如何制作讲解资源包。简单来说,所谓的资源包,就是将游戏的所有资源和素材,进行打包分类,并且进行资源整合,亦或将资源和素材进行压缩,减少游戏体积。

什么是资源包?

我总结了一下,可以从这三个角度来理解什么是资源包。

  • 资源包是一种将游戏的资源和素材进行分类、梳理,并且打包的一种包裹。
  • 资源包可以用来压缩游戏资源和素材,减少游戏体积。
  • 资源包里存在任何可能性,比如它可以包含图片文件、模型文件、音频文件、脚本文件等等,具体要看游戏开发人员的配置需求,来决定资源包里的内容。

现在很多游戏公司都不会编写特殊的资源包格式。因为设计一种资源包格式,需要经过一系列复杂的动作,包括包头、包身和包尾。

关于这个格式的设计,一会儿我会给你仔细分析。因为,和我们自定义网络协议包一样,一个好的资源包,能够很方便进行解包、打包、删除文件、插入文件的操作,以及游戏的在线更新、补丁更新、资源包的解包、打包、删除、插入、更新文件等操作。

而一个好的资源包格式,不会占用主程序大量的时间。因为在游戏中,需要直接读取包文件里面的内容。

比如我们之前在 Pygame 中读取的图片文件,在包裹格式中,可能会这么写伪代码:

1
2

load.image(package.pack/plane.png)    

其中 package.pack 就是包裹,plane.png 是存在在包裹里面的其中一幅图片文件。这样,打了包裹后的文件,就不会污染目录。一般一个包裹文件中存在大量资源,而我们只要按照包裹路径读取就可以了。

如果不编写特殊的资源包格式,那应该怎么制作资源包呢?答案是,使用现成的压缩软件库,进行打包压缩,直接在程序内使用。比如我们最常用的 zip 文件、rar 文件,都是可以拿来做资源包文件的。在 Python 中有内置 zip 模块,可以直接读取 zip 文件。我们可以直接在 Pygame 中结合 zip 模块进行编程。

资源包的格式

我们要讲解的是资源包的制作,我将会用一种较为通用和简单易懂的方法,解释资源包都包含哪些内容,同时让你理解资源包是怎么制作的。

首先,从编程的格式来理解资源包,你需要了解下列这些内容。

  • 资源包头,是一种标记,存放在包裹里最开始的几个字节,一般是 2~4 个字节。资源包头可以用来辨别这个资源包是哪个公司出品的。例如我后面准备举的一个例子,这里面就有 INFO 这样的标记,INFO 可能是这家游戏公司的名字或者是缩写等等。
  • 资源包版本,这个不是必须的。如果考虑到一款游戏各个版本之间变化很大,未来可能会修改资源包的格式,那么这个时候就需要版本号。版本号一般会使用 2 个字节的 short 类型来存储,或者直接用十六进制编辑器能看明白的字符串,来代表版本号,比如用 10 表示 1.0。所以,结合资源包头,我们现在所看到的结构是 INFO10。
  • 资源包是否进行压缩,这个也不是必需的,但是有一些资源包会说明白,究竟是不是压缩资源包。如果是压缩就是 1,不是压缩就是 0。至于压缩格式,一般在编程的时候,就会指定清楚,不需要特别说明在资源包内。
  • 资源包的目录结构以及素材名文件名偏移量,资源包内的目录结构都是虚拟的,所以你可以定义在资源包内类似于 /game/res 这样的目录结构。但是事实上,这只是方便程序调用,事实上目录是不存在的,这是一种只存在在包裹内的虚拟目录。

然后,我们需要规定素材的文件名偏移量。比如 /game/res/background.jpg。这是告诉我们在 /game/res 虚拟目录下,拥有 background.jpg 这个文件。随后需要告诉程序偏移量是多少,一般是存储 4 个字节的整型数字。

到目前为止,资源包的格式看起来可能是这样的:

1
2

INFO100/game/res/background.jpg,[四个字节的偏移量]

在这里,我们看到,偏移量之前多加了一个逗号“,”。这是一个分隔符,也就是告诉程序,这一段在哪里结束。

随后是四个字节的偏移量。所谓的偏移量,就是告诉程序,你要到这个包裹的第几个字节去寻找这个文件的具体内容。

  • 资源包的素材本体。每个本体都可能是一个二进制文件、文本文件或其他任何文件。这些文件的文件名在资源包的素材文件名中都被定义好了。在资源包的素材本体中,我们可能会碰到各种各样的二进制字符,那么我们怎么知道这些素材是从哪里开始哪里结束的呢?
  • 资源包的素材长度,规定素材的长度有两种方法,一种方法是在定义资源包的目录结构以及素材偏移量的时候,再加上一个素材长度,也是四个字节的整型数字。这种方法的好处是,不需要添加某个分隔符告诉程序,这个素材的本体到这里结束。第二种方法是在本体结束的位置添加分隔符,比如一个逗号或者分隔符号|。这种方法的好处是,不需要知道文件长度是多少。但是坏处是,分割符号可能会和素材本体重叠。 比如素材的本体是个二进制文件,分隔符比如是!@#,素材的本体里面也存在!@#,素材的本体里面也存在!@#