gitbook/从0开始学游戏开发/docs/10106.md
2022-09-03 22:05:03 +08:00

111 lines
9.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 第14讲 | 如何制作游戏资源包和保存机制?
我们要做一款打飞机游戏,里面有飞机图片、背景图片、飞机音效、碰撞音效等等非常多的素材。如果将这些资源都放置在一个目录下,将会变得非常混乱。如果按照素材内容来划分目录,程序读取的效率就不高,所以我们需要将这些素材打包在一个资源包内,然后将每个素材都放置在一个虚拟目录内。
因此,今天我们就来如何制作讲解资源包。简单来说,所谓的资源包,就是将游戏的所有资源和素材,进行打包分类,并且进行资源整合,亦或将资源和素材进行压缩,减少游戏体积。
## 什么是资源包?
我总结了一下,可以从这三个角度来理解什么是资源包。
* 资源包是一种将游戏的资源和素材进行分类、梳理,并且打包的一种包裹。
* 资源包可以用来压缩游戏资源和素材,减少游戏体积。
* 资源包里存在任何可能性,比如它可以包含图片文件、模型文件、音频文件、脚本文件等等,具体要看游戏开发人员的配置需求,来决定资源包里的内容。
现在很多游戏公司都不会编写特殊的资源包格式。因为设计一种资源包格式,需要经过一系列复杂的动作,包括包头、包身和包尾。
关于这个格式的设计,一会儿我会给你仔细分析。因为,和我们自定义网络协议包一样,一个好的资源包,能够很方便进行解包、打包、删除文件、插入文件的操作,以及游戏的在线更新、补丁更新、资源包的解包、打包、删除、插入、更新文件等操作。
而一个好的资源包格式,不会占用主程序大量的时间。因为在游戏中,需要直接读取包文件里面的内容。
比如我们之前在Pygame中读取的图片文件在包裹格式中可能会这么写伪代码
```
load.image(package.pack/plane.png)
```
其中package.pack就是包裹plane.png是存在在包裹里面的其中一幅图片文件。这样打了包裹后的文件就不会污染目录。一般一个包裹文件中存在大量资源而我们只要按照包裹路径读取就可以了。
如果不编写特殊的资源包格式,那应该怎么制作资源包呢?答案是,**使用现成的压缩软件库,进行打包压缩,直接在程序内使用**。比如我们最常用的zip文件、rar文件都是可以拿来做资源包文件的。在Python中有内置zip模块可以直接读取zip文件。我们可以直接在Pygame中结合zip模块进行编程。
## 资源包的格式
我们要讲解的是资源包的制作,我将会用一种较为通用和简单易懂的方法,解释资源包都包含哪些内容,同时让你理解资源包是怎么制作的。
首先,从编程的格式来理解资源包,你需要了解下列这些内容。
* **资源包头**是一种标记存放在包裹里最开始的几个字节一般是24个字节。资源包头可以用来辨别这个资源包是哪个公司出品的。例如我后面准备举的一个例子这里面就有INFO这样的标记INFO可能是这家游戏公司的名字或者是缩写等等。
* **资源包版本**这个不是必须的。如果考虑到一款游戏各个版本之间变化很大未来可能会修改资源包的格式那么这个时候就需要版本号。版本号一般会使用2个字节的short类型来存储或者直接用十六进制编辑器能看明白的字符串来代表版本号比如用10表示1.0。所以结合资源包头我们现在所看到的结构是INFO10。
* **资源包是否进行压缩**这个也不是必需的但是有一些资源包会说明白究竟是不是压缩资源包。如果是压缩就是1不是压缩就是0。至于压缩格式一般在编程的时候就会指定清楚不需要特别说明在资源包内。
* **资源包的目录结构以及素材名文件名偏移量**,资源包内的目录结构都是虚拟的,所以你可以定义在资源包内类似于/game/res这样的目录结构。但是事实上这只是方便程序调用事实上目录是不存在的这是一种只存在在包裹内的虚拟目录。
然后,我们需要规定素材的**文件名**和**偏移量**。比如/game/res/background.jpg。这是告诉我们在/game/res虚拟目录下拥有background.jpg这个文件。随后需要告诉程序偏移量是多少一般是存储4个字节的整型数字。
到目前为止,资源包的格式看起来可能是这样的:
```
INFO100/game/res/background.jpg,[四个字节的偏移量]
```
在这里,我们看到,偏移量之前多加了一个逗号“,”。这是一个**分隔符**,也就是告诉程序,这一段在哪里结束。
随后是四个字节的偏移量。所谓的**偏移量**,就是告诉程序,你要到这个包裹的第几个字节去寻找这个文件的具体内容。
* **资源包的素材本体**。每个本体都可能是一个二进制文件、文本文件或其他任何文件。这些文件的文件名在资源包的素材文件名中都被定义好了。在资源包的素材本体中,我们可能会碰到各种各样的二进制字符,那么我们怎么知道这些素材是从哪里开始哪里结束的呢?
* **资源包的素材长度**,规定素材的长度有两种方法,**一种方法**是在定义资源包的目录结构以及素材偏移量的时候,再加上一个素材长度,也是四个字节的整型数字。这种方法的好处是,不需要添加某个分隔符告诉程序,这个素材的本体到这里结束。**第二种方法**是在本体结束的位置添加分隔符,比如一个逗号或者分隔符号|。这种方法的好处是,不需要知道文件长度是多少。但是坏处是,分割符号可能会和素材本体重叠。
比如素材的本体是个二进制文件,分隔符比如是!@#$,素材的本体里面也存在!@#$这样的内容,这样的情况下,就会出现读取中断,因为程序以为素材内的!@#$就是结束符号,事实上这只是素材本身的内容而已。
* **资源包结束符**,这个也不是必须的。我们要结束资源包,必须在资源包的结尾添加结束符,这个结束符是告诉程序,资源包已经结束了。
我们来看一个完整的资源包,大概是什么样子的。
```
[资源包头][版本号][是否压缩][资源包目录/素材文件名A][文件A偏移量][文件A长度]…[资源包目录/素材文件名N][文件N偏移量][文件N长度][素材A本体]….[素材N本体][结束符]
```
了解了资源包的格式内容我们可以很方便地利用Python或者C语言等来编写相应格式的资源包。
我来给这部分做一个总结:
资源包的存在,有两个目的,一是让游戏目录干净整洁,不然看上去都是乱七八糟的图片和各种配置,二是让游戏程序能更快地从内存中读取游戏资源制作的包裹文件,加速游戏的运行效率。这个包裹文件中含有虚拟目录、资源、资源位置、资源名字等等信息。我们不需要从文件目录中去读取单一文件,只需要从内存中载入的资源包中取出某个文件即可。
## 如何制作游戏的保存机制?
每一个游戏几乎都有保存和载入的机制。首先你需要知道,只有保存了数据,我们才能载入数据。那么游戏的保存机制是怎么做的呢?
事实上,游戏的保存和游戏的地图编辑器中保存地图的原理,可以说是异曲同工。如果一个游戏中,有地图、坐标、人物、装备、分数,这些都需要被记录下来,那么我们不可能将地图、坐标、人物、装备、分数等全部转换成二进制文件记录下来。那应该怎么做呢?
首先如果是记录地图有地图1或者地图2我们只需要记录地图的ID就好了。假如是地图2坐标是xy。人物只需要记录人物的ID再关联到人物。一个游戏中玩家建立了一个人物角色就会将这个人物角色进行保存不至于丢失人物角色。所以在读取游戏的时候需要先读取人物角色再读取保存的游戏内容。
至于分数就很好记录了,记录分数其实就是记录数字,所以记录起来会很方便。
那么装备呢如果是装备一般会将装备的所有内容记录下来如果做得精致的游戏还会将地图中那些掉落的装备和死去的NPC进行记录。
还有一种做法是,将游戏保存的文件直接导出成一个脚本文件,以后每次读取数据就只需要使用程序读取脚本就可以了。
## 小结
今天我讲解了资源包的制作以及游戏进度的保存,你需要你记住这些内容。
* 制作资源包的目的是为了厘清游戏素材以及游戏素材的存放结构。资源包的结构与压缩包的结构比较相似,但是为了更贴合游戏程序读取,会对虚拟目录和素材文件名等,做一些修改。
* 另外,为了方便保存游戏进度,我们可以做成游戏脚本,第二次打开游戏直接载入保存的脚本即可。
给你留一个小思考题吧。
在《GTA》中汽车会有不同程度的损毁当你保存完游戏重新进入的时候汽车又复原了请问这是为什么呢
欢迎留言说出你的看法。我在下一节的挑战中等你!