7. C++实现贪吃蛇小游戏

一、前言

本文主要是带大家实现一个简单的带界面的贪吃蛇小游戏,使用的依旧是EGE库,因为用它来做一些小游戏确实很方便。

建议有一定基础的同学进阶学习,比如:

  1. 你至少需要学会配置EGE库。
  2. 你至少需要理解基本的C/C++程序运行逻辑,简单的语法规则。

过于基础、或者本站其它文章涉及到的内容,本文不会再反复赘述,你可以直接搜索、跳转到对应的章节进行学习即可,大部分时候我都会留下跳转的链接。

至于EGE库的配置,可以在文章制作推箱子小游戏,以及文章引入EGE库都可以找到。

包括这个库的基本使用方法,这两篇文章也都有介绍,最好先看一看,本文不会再对这些细节过多赘述。

最后的实现效果如下:

image-20231014153027875

当然,如果觉得颜色不好看的话,直接在代码里面改就行了。

二、思考游戏逻辑

对于做项目来说,第一步的逻辑思考是很重要的,总的来说,我们这个游戏依旧可以延续老的方法,因为它仍然只是一个二维格子游戏。

只要是二维格子游戏,最简单的方式都是直接先以二维数组作为游戏地图,思考游戏逻辑,最后再将其通过画图、或者贴图片的方式,映射到窗口上。

首先来看游戏的运行流程:

  1. 游戏初始化后,会先生成一条蛇,以及一个随机位置的食物,并且蛇是自动按某个方向移动的。
  2. 可以通过控制蛇上下左右移动,吃食物,来增长自己的身体。
  3. 每次吃食物,会增加得分,得分每到达一个阈值,就会加快蛇的移动速度。
  4. 一旦撞墙、或者撞上自己的身体,游戏就结束了。

先看第一步,生成蛇与食物。

蛇是由许多节点组成的,所以我们就可以将这两者设定为一个东西:

一个蛇身体上的节点或者一个食物,都是一个点,占据多个点组合起来按一定顺序移动,就是蛇组成的逻辑。

假设地图为长宽10*10,初始化蛇有三个节点,那么就可以为三个连续的坐标:(3,0),(2,0),(1,0)。

注意:窗口都是以左上角为圆点,向右为x正轴,向下为y正轴。

此时这三个点中(3,0)点就可以看作蛇的头部,并默认向右移动:

image-20231014175745491

而食物的坐标,则采取随机生成的方式,这可以用C中rand函数实现。

而想要实现自动移动的效果,也并不难,我们只需要每次移动前,从后向前,让后一个节点的坐标等于前一个节点的坐标即可。

比如上图中默认想右移动,那么下一次的移动逻辑就是:

  1. (1,0)为蛇尾,它就可以直接等于它的上一个节点坐标(2,0)。
  2. (2,0)为身体,它同样可以直接等于它的上一个节点坐标(3,0)。
  3. (3,0)为头部,没有上一个节点了,所以它需要根据当前的移动方向更改自己的坐标,比如向右,那么就把自己的横坐标x加一,即(4,0)。

只要不断地调用这一过程,蛇就能完成自动移动了。

然后是第二步,我们需要控制蛇的上下左右移动,实际上也就是上面第一步所说的方向。

因为蛇本身的移动过程是全程自动化的,我们根本不用干预,我们唯一需要干预的是,它下一步要移动的方向,也就是决定蛇头下一步如何更改自己的坐标点。

至于身体,它们会自动跟着蛇头移动,因为它们的坐标就是不断等于上一个节点的坐标,而最顶层的坐标不就是蛇头吗?

吃食物也简单,每次移动完成后,看看自己蛇头是不是和食物坐标相等就行了,如果相等,就说明吃掉了食物,直接在尾部添加一个蛇的节点即可,它会自动跟随前面的节点更改自己的坐标的。

然后来看看第三点:增加得分、增加移动速度。

得分简单,就是在每次吃掉食物的时候,给一个记录得分的全局变量加个数值即可。

至于加快蛇的移动速度,这个需要绕个弯了。

因为前面刚说过,蛇本身是自动移动的,那么一般我们都是将这个过程直接封装为一个函数,然后每隔一段时间来调用一下这个函数即可。

而加快移动速度,其实也就是减少调用移动函数的间隙而已。

比如原本我是1秒调用5次,那就是5格/s的速度,而如果为1s调用10次,那不就10格/s了?

这就可以用休眠函数做到,事实上EGE库提供了一个休眠函数:delay_ms,它的作用就是休眠当前程序多少毫秒,只要把它放在游戏循环里,通过控制它每次休眠的秒数,就能间接实现对蛇的速度控制了。

至于最后就简单了,只要蛇头的坐标超过了我们预定义的地图坐标或者某一刻等于了身体某个节点的坐标,就说明蛇撞了墙或者自己的身体,也就是游戏结束。

自此我们就将游戏的整个运行逻辑稍微梳理了一遍,如果对程序运行流程不太熟悉的同学,可能看起来还是有些吃力,所以下面我们直接来讲解代码嘛吧。

三、代码讲解

因为前面说了,默认看本文章的都是有一定编程基础的同学,如果还带着大家从零一句一句代码的敲实在是有些繁琐的,所以这里采取的是直接讲解代码的方式。

我会尽量将大家可能会感到比较困惑的点都讲解一下。

虽然总体代码量并不大,只有两百来行,但直接粘贴到这里还是有点太占篇幅了,所以我提供的是一个源代码文件,可以通过链接直接点击下载:snake

这仅仅只是一个源代码文件,使用的是vs环境、EGE库,配置方法我在文章开头已经说过了,可以参考本站其它文章,这里不再赘述。

你可以将环境配置好后,直接将这个文件中的源码复制进你的项目中即可使用。

1.运行逻辑

首先我们还是先来理一下代码的总体运行逻辑,便于你能有一个总体的概念,知道它是怎么运行起来的,后面我们再一步一步去讲解其中的细节。

这同样也算是在教大家如何去快速理解别人写的代码。

遇到一个陌生程序,不要被其繁琐的定义吓到,直接来到入口函数即可; image-20231014182909473

我代码里基本都写了注释的,基础强的可能都不用我讲解,也能看明白。

首先这里定义了食物这两个结构体,毕竟它们是我们游戏里面的主角嘛。

Snake snake; //蛇
Point food; //食物所在坐标

这两个结构体一看,你觉得不认识,那就不出意外的肯定是自定义类型了。

这种情况下,先不用管它的具体实现,免得把自己绕晕,我们只需要知道这里一个是蛇、一个是食物即可,这本就是结构体、类封装的本意。

然后是初始化界面、设置标题,这是EGE库的函数,使用起来并不难,前面提到过的推箱子文章中已经对其进行了一定程度的介绍,更多可以参考官方文档:库函数目录

	//初始化界面
	initgraph(g_width, g_height);
	//设置标题
	setcaption("贪吃蛇小游戏");

简单来说,就是用来创建窗口、设置窗口标题的。

而窗口的大小等于这两个变量:g_widthg_height

这种前面带g的,算是一种约定成俗的习惯,即:global,全局的意思,一般会作为全局变量的前缀。

比如还有m_*,前缀带m的,即member,成员变量的意思,一般用作类中变量名的前缀。

而全局变量,正常情况下一般都会写在文件开头、或者其它专门定义全局变量的文件中。

比如我这里就写在了文件开头:

image-20231014183839691

这是为了方便使用,以后你修改游戏地图的大小时,就可以直接修改这里即可。

作者:余识
全部文章:0
会员文章:0
总阅读量:0
c/c++pythonrustJavaScriptwindowslinux