# 高级程序设计大作业报告
# 贪吃蛇大作业
**实验内容**
本次实验的主要内容为使用 C++ 编程语言,使用类的相关知识,构建出一个贪吃蛇小游戏,该小游戏应当具备有三种基础功能,并可根据 OJ 的提示,添加更多的加分项。
**设计思路与功能描述**
**设计思路说明**
**贪吃蛇游戏设计思路说明**
由于本次实验希望采用面向对象的思想来进行程序的编写,而贪吃蛇游戏本身作为程序的出题,故应当先绘制出该程序的类图,再在该类图的基础上进行进一步程序的编写。
由于不同玩法的贪吃蛇之间略有不同,故在此我们首先对“入门版”的贪吃蛇类图进行说明。
![](https://www.writebug.com/myres/static/uploads/2021/11/5/3594eba7989f35e66b4277f5c871a332.writebug)
在本程序中,一场贪吃蛇游戏的进行应当是基于 Game 的某一子类展开的,在“入门版”中,这个子类为 Game1。
通过生成一个 Game1 实体,依次调用该实体的 init_game()方法和 init2()方法,可以在画布上画出初始的地图,并生成一个 snake 实体,同时藉由 refresh()方式生成一批 fruit 实体。
在初始化结束后,游戏可以正式开始,在游戏进行的过程中,通过 listen()方法对键盘进行实时监听,一旦用户执行了某个操作,就将操作值进行记录,在下一次调用 play()方法时,判断该值对下一步结果的影响,最后调用 draw()方法在画布上更新界面。
而当 play()方法发现下一步蛇会死亡时,会弹出“游戏结束”字体,最后调用 back()方法,回到初始菜单。
基于上述类图与大体流程描述,一下对各个类进行逐一详细描述:
①Game 类
该类是所有游戏类的父类,其主要功能是为各个子类提供一个统一的初始与结束模板,使得各个游戏类的调用、结束显得较为统一。
该类中的属性有:
```c++
width
```
int 类型,默认值为 800。该属性是新建画布时,所建立的画布宽度,在本次程序设计中,这一初始值没有进行过改变。
```c++
length
```
int 类型,默认值为 600。该属性与 width 类似,是初始新建立画布的高度,在本次程序设计中,也未进行过改变。
该类中的方法有:
```c++
init_game()
```
void 返回类型。该方法是在初始化地图前必须进行的调用,作用是生成指定规格的画布。
```c++
back()
```
void 返回类型。该方法是在游戏结束(包括按 Q 键退出、游戏失败录入姓名后退出等)后,生成了一个 Menu 类实体,重新回到主页面。
```
init2()、listen()
```
Game 类作为一个大的父类,还额外定义了 init2()和 listen()这两个虚函数,用于在编写子类时记得实现这两个重要的方法。
**②Game1 类**
该类是 Game 类的子类,也是其他 Game 类用以修改功能的基础,如何利用一个 Game1 类实体进行一场游戏已在前文叙述得较为清晰,故不在此赘述,这里将重点对 Game1 类中的众多属性和方法进行详细说明。
该类中的属性有:
**map**
char型二维数组,规格为[40][30]。这一大小对应了整个贪吃蛇游戏界面的40×30的规格,这一属性也是Game1类中众多方法实现的基础。map数组中存放的字符是游戏界面中,对应位置方格.jpg图片文件名的第二个字符。例如'0'表示这里是空草坪,'7'表示这里是食物等,该数组中可存放的字符会随着后续功能的拓展而发生变化。
**mes**
char型字符,默认值为'u'。mes用于记录用户上一次对贪吃蛇发出的控制指令,其中'u'表示向上,'d'表示向下,'l'表示向左,'r'表示向右。
**head**
snake 型指针。由于贪吃蛇在本游戏中是以链表的形式存放的,即将每一个蛇段看作一个实体,所以为了能够对蛇进行控制,就需要掌握链表的表头——即蛇头的相关信息。
**area**
int 类型,初始值为 28×38。该值用于记录当前地图上还能放置水果的位置总数,以便随机生成新水果,并在后续功能中辅助判定地图是否已满。
**length**
int 类型,初始值为 3。该值用于记录当前蛇的长度,以便在 UI 界面进行显示。
**life**
int 类型,初始值为 1。该值用于记录蛇的生命数并在 UI 上显示,在 Game1 的游戏中这个值没有意义,但在后续加入的其他功能中,该值可以记录蛇的最多死亡次数。同时,当 life 变为 0 时,默认是游戏结束的标志,在这种情况下,play()方法将被禁用。
**score**
int 类型,初始值为 0。该值用于记录当前的得分并在 UI 上显示,同时该值也会在排行榜记录时被录入文件。
**highscore**
int 类型。该值在 init2()方法下的 getrecord()方法中被赋值,用于记录当前所完游戏种类在排行榜中的最高分,当 score 高于 highscore 时,highscore 在么一个游戏刻被刷新为 score 值。
该类中的方法有:
**refresh()**
void返回类型。该方法在初始化地图与fruit_num为0时被调用,该方法可以在当前为空草坪(即map值为'0')的位置随机生成1~5个fruit实体,并同时刷新fruit_num值与area值。
**draw()**
void 返回类型。该方法在每一次操作执行结束后进行调用,该方法会逐一遍历 map 数组,使用 easyX 自带的图形辅助函数将每一个数组元素所代表的图片样式绘制在画布的指定位置。
**getrecord()**
void 返回类型。该方法在地图初始化时会被调用。该方法会通过遍历的方式逐行从 record.txt 中读取游戏记录,在游戏版本编号和当前游戏一致的记录中找寻得分最高的成绩赋值给 highscore 属性。
**init2()**
void返回类型。该方法是重写了父类Game的方法所得,会在新建好画布之后进行调用。该方法会对map属性进行赋值,将整个地图的边界都赋值为“硬墙”(map编号为'8'),同时调用refresh()方法,生成一批食物,并将食物所在位置赋值为“食物”(map编号为'7')。
同时该方法还会新建一个 snake 实体,其长度为 3,蛇头在[20][14]位置,蛇尾在[20][16]位置,并在 map 中对对应位置赋值。在完成了对 map 的初始化赋值后,调用 draw()方法,绘制初始界面。
**play()**
bool 返回类型。该方法用于处理在下一个游戏刻时游戏发生变化的逻辑,其过程是先通过 snake 类的 next_pos()方法,获取到下一个游戏刻蛇头的位置,然后对该位置进行判定。
如果该位置是蛇尾以外的蛇身、硬墙,则游戏结束,弹出提示语,同时将 life 归零,禁用 play()方法,恒返回 false;若该位置是果实,则调用该 snake 的 eat()方法,同时让 length 属性自增 1,返回 true;若该位置是空草坪,则 调用 snake 的 move()方法,返回 true;
返回值适用于标识游戏是否能够进行下去的判断依据。
**record()**
void 返回类型。该方法用于在游戏结束或用户自行退出时调用,弹出提示语要求用户输入姓名,同时对用户输入的内容进行实时显示。当用户输入完毕后,在 record.txt 的末尾追加本次游戏的版本编号、用户名、成绩。
**drawui()**
void 返回类型。该方法用于在每一次 draw 方法调用后,紧接着调用该方法绘制页面下部的所有 UI 提示交互。其实现逻辑是使用 int2char 函数,将蛇的长度、生命数、分数等存储为 int 类型的信息与提示语一并合成为一个 char 数组。然后再在指定的位置输出这些提示字符串。
**listen()**
void 返回类型。该方式重写了父类 Game 的 listen 方法。该方法可以实时对键盘内容进行实时监听,当有按键按下时,采�