入门级c语言贪吃蛇教程

编程入门 行业动态 更新时间:2024-10-27 22:24:30

<a href=https://www.elefans.com/category/jswz/34/1708332.html style=入门级c语言贪吃蛇教程"/>

入门级c语言贪吃蛇教程

前言:本人也是c语言初学者,所以很能理解初学者在编写程序时遇到各种问题的无奈,在网上的各种贪吃蛇教程,要么有很多额外的知识,要么甚至都不能正常运行,所以决定自己做一个贪吃蛇小游戏,这也是我做的第一个小项目贪吃蛇,希望能以浅显的知识和思路讲清楚我的编写过程,其中也许会有一些错误或者不够严谨的地方请大家指正。

编程语言:c语言

编译环境:VS2022

设计基本思路:

首先来分析一下游戏的基本过程,首先在一定范围的地图上会有边界(墙),在某个初始位置有一条拥有初始长度的贪吃蛇,利用键盘控制蛇头方向,蛇头吃到食物,蛇长度会增加一个单位,蛇头撞到墙会导致游戏结束,其次蛇头撞到蛇身也会导致游戏结束。

利用控制台本身就能做到这几点,无需使用额外的图形绘制窗口,不然会增加很多的负担,比如去学习easyx,我反正是懒得去学这些额外的东西,但是如果有兴趣的可以去使用easyx绘制一个更加美观的贪吃蛇。

利用print()函数可以在控制台打印字符,但是贪吃蛇是动态的,这也就意味着控制台的内容也必须一直在变化,那么为了实现这一点,应当有两种方法:

,我首先想到的是利用system("cls")来清除控制台所有的内容,然后再打印一次变化之后的所有内容,这样会很方便,但是问题也很明显,那就是控制台会频闪,对眼睛可以说是非常的折磨,所以为了消除频闪,我又去了解控制台的双重缓冲,简单来说就是预先计算好下一帧的内容,然后直接替换上一帧的内容,只要帧率够高,就不会出现频闪的情况,想法很美好现实很残酷,事实上去学习双重缓冲完全与设计理念背道而驰,不仅很多内容看不懂,而且浪费了大量的时间精力。

,第二种方法就是利用gotoxy()来控制光标的位置,以达到变化的要求,此时我们已经可以控制光标的位置了,也就是说很多内容比如墙,就不需要打印第二次,那么变化的就只有贪吃蛇和食物的位置了。这里需要用到一个很重要的格式符,即退格符\b,这里举个例子说明作用:假设我们用  /  来表示光标的位置,一串字符为123456709,我们要使这串字符变成123456789,首先可以利用gotoxy来使光标移动到9前面,即12345670/9,然后printf("\b8"),就变成了1234567/89,这里有两个操作,首先打印了退格符\b使光标往左移动了一格并且覆盖掉了9前面的0,然后又打印了一个数字8代替了0原本的位置。(还不懂的话可以去自行查找资料,这是非常好的习惯,后面的内容我可能不会讲的非常详细,所以不懂的话就需要读者自行去查找资料了解,这也是为了节省篇幅)

那么好,现在我们已经拥有了强大的工具gotoxy函数和退格符\b,利用这两个工具已经完全足够实现动态的效果了。众所周知贪吃蛇移动可以简化为,蛇头前进一格,蛇尾前进一格,也就是中间的身体是不需要变化的,那么就只需要把蛇尾消除,把蛇头变成蛇身,再把下一格变成蛇头,就完成一次移动了,假如蛇头吃到食物也很简单,蛇尾不消除即可。

思路有了,现在可以开始着手实现了。

具体实现:

这里我觉得应该加快进度了,那么就不会去纠结一些细节了,免得太过啰嗦。

绘制一个地图:

我们的地图中一共有四种不同的字符,代表每一格不同的状态,比如

墙代表1,蛇代表2,食物代表3,空代表4

首先利用define定义一下宽度和长度这两个常量

#define width 30
#define height 20

由于地图是二维的,所以用一个二维数组就能很完美的记录地图每一个位置的状态

int map[height+2][width*2+3];//储存每个位置的状态,其中!!!墙1蛇2食物3空4!!!

二维数组是从第map[0][0]开始的,这里需要注意。

这里width和height我的理解是蛇能够运动的范围,而不包括墙,所以需要数组的行数为height+2

而列数为什么是width*2+3,是因为美观,因为控制台上下行有间隔,而左右列中间是没有间隔的,所以在列与列之间加入一个空格,会使界面不会太紧凑,这非常影响游戏体验所以是需要美化一下的。

接下来需要初始化一下这个地图,也就是二维数组,我希望蛇的初始长度为3(包括蛇头),然后蛇的初始位置在地图的左上角,整个蛇的方向向右。

int length = 3;//初始化蛇长度为3,位置位于左上角第一行
int headx = length*2, heady = 1;
int tailx = 2, taily = 1;
void initmap()//初始化地图,不初始化食物
{for (int y = 0; y < height + 2; ++y){for (int x = 0; x < width*2+3; ++x){if (x == 0 or x == width*2+2 or (y == 0 or y == height + 1) and x % 2 == 0)map[y][x] = 1;else if((x == 2 or x == 4 or x == 6) and y == 1)map[y][x] = 2;elsemap[y][x] = 4;}}
}

这里创建一个函数用于初始化地图,创建了一个长度变量,两个变量表示蛇头位置,两个变量表示蛇尾位置,后面会说明原因,其他贪吃蛇教程或许会使用结构体,我觉得没有必要,所以宁愿使用这种啰嗦的方法,但是确实很简单也很有效。

控制台的坐标和平面直角坐标系不同,这里的y坐标正方向是向下的,也就是说y-1表示向上一格,y+1才表示向下。同样的左上角第一格的坐标也是0,0。

利用两个for循环就可以初始化一个二维数组,加入判断条件使墙的位置定义为1,蛇的位置定义为2,空为4

接下来打印地图

void drawmap()//绘制地图,不绘制食物
{system("cls");for (int y = 0; y < height + 2; ++y){for (int x = 0; x < width*2+3; ++x){if (map[y][x] == 1)printf("■");else if (x == headx and y == heady)printf("X");else if (map[y][x] == 2)printf("O");else if (map[y][x] == 4)printf(" ");			}printf("\n");}
}

同样创建一个函数,后面不再赘述。system("cls")作用是清屏,前面有提到,后面会解释。

同样是利用两个for循环,■小正方体表示墙很合适,蛇头就干脆直接用X,蛇身用O,这里仅仅是为了方便区分,没别的意思,然后空直接就用空格表示。

表示每一个蛇身的方向:

为什么需要表示每一个蛇身的方向呢?前面有说到,蛇头仅仅需要把蛇头行进方向的下一格变成蛇头,然后把原来的蛇头换成蛇身即可,这里蛇头位置的变化取决于方向,同样的,蛇尾位置的变化也需要一个方向,要不然我们仅仅只是清除蛇尾,蛇尾的位置到底应该怎么变化呢,向上还是向下,向左还是向右,无从得知,所以需要蛇尾的方向,由于蛇尾一直在消失,所以每一节身体都有可能成为蛇尾,所以需要每一节蛇身的方向。

由于不可能每一个蛇身都创建一个变量,这里可以使用结构体,但是我想到另外一个方法,再创建一个二维数组,和前面二维数组的大小完全一致,只不过这个数组专门用来储存蛇身的方向。

int mapdirec[height+2][width*2+3] = { 0 };//储存蛇身对应位置的方向,!!!方向为左1上2右3下4!!!

同样需要初始化

void direcmap()//初始化蛇身方向
{mapdirec[1][2] = 3;mapdirec[1][4] = 3;
}

这里说明一下,蛇头蛇尾地图状态都是2,只不过用变量把蛇头蛇尾位置标出来了而已,这里3表示方向向右,不初始化蛇头的方向后面会解释

万事俱备,可以编写移动代码了

控制贪吃蛇速度:

在开始移动之前,贪吃蛇会停顿一小段时间,在这段时间内完成获取蛇头方向和停顿的功能

void getdirec_wait()//获取蛇头需要朝向的方向_停顿控制游戏速度
{int num = 0;for (int i = 0; i < speed; ++i){Sleep(5);if (_kbhit()){num = _getch();if (num == 97 or num == 65)//a按下direc = 1;if (num == 119 or num == 87)//w按下direc = 2;if (num == 100 or num == 68)//d按下direc = 3;if (num == 115 or num == 83)//s按下direc = 4;}}
}

利用for循环实现小段时间多次运行的好处就是在停顿的时候也能获取到键盘的输入,不会造成程序堵塞。

这里解释一下,事先创建了两个变量speed和direc。speed变量的值用于控制游戏速度,停顿的时间为5*speed,_kbhit()函数用于检测键盘是否按下,如果按下,用num来储存_getch()函数的返回值,返回的即为按下键的ASCII编码,这里判断条件有两个是因为WASD有大小写。变量direc表示等待期间蛇头需要朝向的方向,即键盘控制的方向,并不一定是蛇头真正的方向,只有当等待时间结束时才会改变蛇头的方向,蛇头真正的方向储存在第二个二维数组中,即

mapdirec[heady][headx]

等待时间结束后,应当先进行判断再移动,因为假如先移动后判断,蛇头就很有可能撞进墙里面或者直接撞进自己的身体里面了。

检测并完成数组状态的更新:

众所周知,控制台展示的内容必须和map数组内容一致,我选择把检测和移动分成两个模块有利有弊,但是理解起来应该不会更难。这一步的目的是完成两个数组的更新,移动模块才负责完成控制台上内容的更新。

void detect()//完成移动蛇之前的所有预备部分
{if (direc != mapdirec[heady][headx] - 2 and direc != mapdirec[heady][headx] + 2){mapdirec[heady][headx] = direc;}if (mapdirec[heady][headx] == 1)//左{if (map[heady][headx - 2] == 1){ broadcast = 1;// 撞墙寄}else if (map[heady][headx - 2] == 2){broadcast = 2;// 撞自己寄}else if (map[heady][headx - 2] == 3){broadcast = 3;//吃到食物辣++length;headx -= 2;map[heady][headx] = 2;mapdirec[heady][headx] = 1;}else if (map[heady][headx - 2] == 4){headx -= 2;map[heady][headx] = 2;mapdirec[heady][headx] = 1;}}else if (mapdirec[heady][headx] == 3)//右{if (map[heady][headx + 2] == 1){broadcast = 1;// 撞墙寄}else if (map[heady][headx + 2] == 2){broadcast = 2;// 撞自己寄}else if (map[heady][headx + 2] == 3){broadcast = 3;//吃到食物辣++length;headx += 2;map[heady][headx] = 2;mapdirec[heady][headx] = 3;}else if (map[heady][headx + 2] == 4){headx += 2;map[heady][headx] = 2;mapdirec[heady][headx] = 3;}}else if (mapdirec[heady][headx] == 2)//上{if (map[heady - 1][headx] == 1){broadcast = 1;// 撞墙寄}else if (map[heady - 1][headx] == 2){broadcast = 2;// 撞自己寄}else if (map[heady - 1][headx] == 3){broadcast = 3;//吃到食物辣++length;heady -= 1;map[heady][headx] = 2;mapdirec[heady][headx] = 2;}else if (map[heady - 1][headx] == 4){heady -= 1;map[heady][headx] = 2;mapdirec[heady][headx] = 2;}}else if (mapdirec[heady][headx] == 4)//下{if (map[heady + 1][headx] == 1){broadcast = 1;// 撞墙寄}else if (map[heady + 1][headx] == 2){broadcast = 2;// 撞自己寄}else if (map[heady + 1][headx] == 3){broadcast = 3;//吃到食物辣++length;heady += 1;map[heady][headx] = 2;mapdirec[heady][headx] = 4;}else if (map[heady + 1][headx] == 4){heady += 1;map[heady][headx] = 2;mapdirec[heady][headx] = 4;}}gotoxy(0, height + 2);printf("                               \r分数:%d  目前最高分:%d",length,maxscore);
}

可能你会说,哇,这么长,没错就是这么长,但是其实都是重复的内容,原理也并不复杂,本人实力有限做不到把这么长的代码优化。因为我使用的都是最基础的条件判断,代码也并不晦涩难懂。

把代码拆分你就明白我的意思了

void detect()//完成移动蛇之前的所有预备部分
{if (direc != mapdirec[heady][headx] - 2 and direc != mapdirec[heady][headx] + 2){mapdirec[heady][headx] = direc;}

首先第一部分的代码很简单,因为我设置的方向是左1上2右3下4,由于贪吃蛇不能往相反方向走,假如不加这一段,贪吃蛇往右走的话,你按A往左走,就直接判断成蛇头撞到蛇身了,这是明显不合逻辑的,我看网上有人的贪吃蛇代码也存在这种问题不修复,让我不禁怀疑作者本人到底玩没玩过贪吃蛇,难道贪吃蛇真的能把头往身体里面缩?

好的不说废话了,这里之所以能防止这一点是因为左1右3之间相差2,上2下4同样相差2,也就是说只要direc和mapdirec[heady][headx]相差不是2,就可以把蛇头方向mapdirec[heady][headx]修改成direc的方向,完成键盘对蛇头方向的控制。

if (mapdirec[heady][headx] == 1)//左{if (map[heady][headx - 2] == 1){ broadcast = 1;// 撞墙寄}else if (map[heady][headx - 2] == 2){broadcast = 2;// 撞自己寄}else if (map[heady][headx - 2] == 3){broadcast = 3;//吃到食物辣++length;mapdirec[heady][headx] = 1;headx -= 2;map[heady][headx] = 2;mapdirec[heady][headx] = 1;}else if (map[heady][headx - 2] == 4){mapdirec[heady][headx] = 1;headx -= 2;map[heady][headx] = 2;mapdirec[heady][headx] = 1;}}

再观察下面的代码你会发现规律,这是四部分代码,分别判断mapdirec[heady][headx]等于1 2 3 4的情况,这里取等于1的情况来说明。

等于1说明目前蛇头的方向是向左的,那么接下来有四种可能,对应蛇头左边那格的四种状态,假如蛇头左边是墙,那就说明蛇头已经撞到墙,游戏应当结束了,所以创建一个broadcast变量,来告诉下一个函数,游戏已经结束了不用继续运行了。

第二种可能就是撞到蛇身,同样游戏结束,用broadcast等于1或2来说明不同的死法。第三种可能就是吃到食物,这是也需要通知后面的函数不需要消除蛇尾,第四种也就是撞到空气,啥都没有发生,那就需要消除蛇尾。下面三段代码的结构是完全相同的,只不过因为蛇头方向不同,蛇头位置坐标变化也不同。这里注意,以撞到空气的情况为例

else if (map[heady][headx - 2] == 4){headx -= 2;map[heady][headx] = 2;mapdirec[heady][headx] = 1;}

两个数组都需要更新,map更新为2表示蛇,方向设定为1表示向左。

gotoxy(0, height + 2);
printf("                               \r分数:%d  目前最高分:%d",length,maxscore);

这一小段呢可以不用管,作用就是在地图下面增加一个显示分数的文字,至于前面为什么会有那么长的空格,以及\r有什么用,纯粹是因为我不知道怎么把一行文字覆盖掉,就用一大堆空格强行覆盖掉了,没事小问题,不要在意这些细节。

好了啰嗦了这么久终于可以编写移动模块了

移动模块:

移动模块不仅负责控制台的更新,还负责蛇尾坐标的更新。也许你会问为什么蛇尾坐标的更新不放在前面的检测模块,是因为如果把蛇尾部分的代码放进检测,代码会变的非常长,会出现大量的重复,除非另外再创建一个蛇尾更新的模块,没有什么必要。

void move()//蛇移动的代码
{gotoxy(headx + 1, heady);printf("\bX");if (mapdirec[heady][headx] == 1){gotoxy(headx + 3, heady);printf("\bO");}else if (mapdirec[heady][headx] == 3){gotoxy(headx - 1, heady);printf("\bO");}else if (mapdirec[heady][headx] == 2){gotoxy(headx + 1, heady + 1);printf("\bO");}else if (mapdirec[heady][headx] == 4){gotoxy(headx + 1, heady - 1);printf("\bO");}if (broadcast == 3){food();if (length == width * height)broadcast = 4;}else {gotoxy(tailx + 1, taily);printf("\b ");map[taily][tailx] = 4;if (mapdirec[taily][tailx] == 1){mapdirec[taily][tailx] = 0;tailx -= 2;}else if (mapdirec[taily][tailx] == 2){mapdirec[taily][tailx] = 0;taily -= 1;}else if (mapdirec[taily][tailx] == 3){mapdirec[taily][tailx] = 0;tailx += 2;}else if (mapdirec[taily][tailx] == 4){mapdirec[taily][tailx] = 0;taily += 1;}}broadcast = 0;
}

同样分成两个部分,第一部分用于更新蛇头的位置,就不多说了,第二部分有两种情况,假如broadcast是3那就不消除尾巴,同时需要更新食物的位置并打印,这里用一个food()函数代替方便之后使用。至于判断条件。即为结局增加的一个小彩蛋,假如你真的能把地图填满,那就通关了,同样获得的也一定是无法超越的最高分,用broadcast = 5来说明。

食物模块:
void food()//移动食物的位置
{srand((unsigned)time(NULL));int num = rand();do{srand((unsigned)num);foodx = (rand() % (width - 1) + 1)*2;srand((unsigned)(num + 1));foody = rand() % (height - 1) + 1;num += 2;} while (map[foody][foody] != 4 or map[foody][foodx] == 2);gotoxy(foodx + 1, foody);printf("\bG");map[foody][foodx] = 3;}

这里就稍显复杂了,因为有很多看不懂的代码,不过不用慌,这里简单介绍。

因为食物的出现是随机的,所以需要至少两个随机数来表示x坐标和y坐标,srand()和rand()都与获取随机数有关,这里用到time()函数来取随机数。随机数的范围是设计好的,同样用width和height来表示,这样以后需要修改地图的大小的时候,直接找到最前面define定义的常数修改即可,非常的方便。

do while的作用和while不同,前者是先运行再判断是否继续运行,后者是先判断后运行,前者至少会运行一次。用在这里就很合适,先运行一次,假如地图上这个位置被蛇身占用了,那就需要再取一次随机数,再继续判断,之所以这里有两个判断条件用or连接,看似两个必定同真同假,但是只写一个的时候会出现意想不到的bug,这样修改之后就没问题了(至少目前为止没发现问题)。

一直到移动模块结束之后,此时已经完成一个完整的循环了,移动之后就等待,然后再检测,再移动,所以现在可以编写游戏主体函数了,也就是把这些模块全部连起来。

游戏主体函数:
void game()
{gotoxy(0, height + 2);printf("按下D或S以开始游戏");while (direc == 0){name();int num = 0;Sleep(5);if (_kbhit()){num = _getch();if (num == 100 or num == 68)//d按下direc = 3;if (num == 115 or num == 83)//s按下direc = 4;}}mapdirec[heady][headx] = direc; food();while (1){name();detect();if (broadcast == 1 or broadcast == 2)break;move();if (broadcast == 4)break;getdirec_wait();}
}

这里有两个循环,name()函数先不管,第一个循环的作用就是一直运行直到键盘按下D或者S开始游戏,因为初始的direc设置的是0不对应任何一个方向,所以只要direc不等于0即进入下一个循环,进入下一个循环之前先设置蛇头的方向以及更新食物的位置。

第二个循环,因为开始游戏时已经选择了方向,相当于等待时间结束,此时直接进入检测模块,检测完之后,条件判断游戏是否结束,结束的话就用break跳出循环,没有就进入移动模块,然后再判断游戏是否通关。这里也能体现检测模块和移动模块分开的一个好处就是,假如合在一起,那这些代码必须集成到一段超级长的代码里面,会很不方便,甚至会产生一些意想不到的问题。移动模块结束之后相当于一个循环就结束了,此时进入等待时间。

游戏结束模块:

这里编写break之后的函数,也比较简单,只不过还加上了一个再来一次的功能,用broadcast = 5来表示

void over()
{gotoxy(0, height + 2);printf("\r ");if (broadcast == 1){		printf("撞到墙了!!!游戏结束,本次得分为%d",length);		}else if (broadcast == 2){		printf("撞到自己了!!!游戏结束,本次得分为%d",length);		}    else if (broadcast == 4){printf("你也太厉害了叭!!!游戏通关,本次得分为最高分%d", length);}if (length > maxscore)maxscore = length;gotoxy(0, height + 3);printf("按1再来一次\n");int num;scanf("%d", &num);if (num == 1){system("cls");broadcast = 5;}elsebroadcast = 0;
}
一些函数的补充:

关于gotoxy()移动光标的函数我也是查的,以及隐藏光标的函数,不然玩的时候光标跳来跳去的。还有游戏开始时控制速度的函数,游戏结束时是否再来的函数,都比较简单了就不多说了,都放在一起。

gotoxy()函数:

void gotoxy(int x, int y)//这里是移动光标的函数
{HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);COORD pos = { x,y };SetConsoleCursorPosition(handle, pos);
}

HideCursor()函数:

void HideCursor()//这里是隐藏光标的函数
{CONSOLE_CURSOR_INFO cursor_info = { 1,0 };SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}

name()函数:

void name()
{system("title 贪吃蛇");
}

仅仅只是用来修改控制台左上角的名字,可有可无

选择速度的函数:

void ready()
{int num;broadcast = 0;length = 3;headx = length * 2, heady = 1;tailx = 2, taily = 1;direc = 0;printf("请选择游戏难度,慢请选1,正常请选2,真正的挑战请选3\n");scanf("%d", &num);if (num == 1)speed = 20;else if (num == 2)speed = 10;else if (num == 3)speed = 5;
}
main()函数:

相当于把前面所有的函数都放在这里了

void main()
{do{ready();HideCursor();initmap();direcmap();drawmap();game();over();	} while (broadcast == 5);
}

没错实际上main函数只有这么一丁点,同样用一个do while来判断broadcast是否等于5以决定是否再来一次

最终代码:

前面并没有说明需要用到的头文件,这里直接展示最终代码吧

#include<stdio.h>
#include<time.h>
#include<Windows.h>
#include<conio.h>
#include<stdlib.h>
#define width 30
#define height 20
int length = 3;//初始化蛇长度为3,位置位于左上角第一行
int headx = length*2, heady = 1;
int tailx = 2, taily = 1;
int foodx, foody;
int map[height+2][width*2+3];//储存每个位置的状态,其中!!!墙1蛇2食物3空4!!!
int mapdirec[height+2][width*2+3] = { 0 };//储存蛇身对应位置的方向,!!!方向为左1上2右3下4!!!
int direc = 0;//蛇头需要朝向的方向
int broadcast = 0;//告诉你寄的原因
int speed = 10;
int maxscore = 0;
void name()
{system("title 贪吃蛇");
}void initmap()//初始化地图,不初始化食物
{for (int y = 0; y < height + 2; ++y){for (int x = 0; x < width*2+3; ++x){if (x == 0 or x == width*2+2 or (y == 0 or y == height + 1) and x % 2 == 0)map[y][x] = 1;else if((x == 2 or x == 4 or x == 6) and y == 1)map[y][x] = 2;elsemap[y][x] = 4;}}
}void drawmap()//绘制地图,不绘制食物
{system("cls");for (int y = 0; y < height + 2; ++y){for (int x = 0; x < width*2+3; ++x){if (map[y][x] == 1)printf("■");else if (x == headx and y == heady)printf("X");else if (map[y][x] == 2)printf("O");else if (map[y][x] == 4)printf(" ");			}printf("\n");}
}void direcmap()//初始化蛇身方向
{mapdirec[1][2] = 3;mapdirec[1][4] = 3;
}void getdirec_wait()//获取蛇头需要朝向的方向_停顿控制游戏速度
{int num = 0;for (int i = 0; i < speed; ++i){Sleep(5);if (_kbhit()){num = _getch();if (num == 97 or num == 65)//a按下direc = 1;if (num == 119 or num == 87)//w按下direc = 2;if (num == 100 or num == 68)//d按下direc = 3;if (num == 115 or num == 83)//s按下direc = 4;}}
}void HideCursor()//这里是隐藏光标的函数
{CONSOLE_CURSOR_INFO cursor_info = { 1,0 };SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}void gotoxy(int x, int y)//这里是移动光标的函数
{HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);COORD pos = { x,y };SetConsoleCursorPosition(handle, pos);
}void food()//移动食物的位置
{srand((unsigned)time(NULL));int num = rand();do{srand((unsigned)num);foodx = (rand() % (width - 1) + 1)*2;srand((unsigned)(num + 1));foody = rand() % (height - 1) + 1;num += 2;} while (map[foody][foody] != 4 or map[foody][foodx] == 2);gotoxy(foodx + 1, foody);printf("\bG");map[foody][foodx] = 3;}void detect()//完成移动蛇之前的所有预备部分
{if (direc != mapdirec[heady][headx] - 2 and direc != mapdirec[heady][headx] + 2){mapdirec[heady][headx] = direc;}if (mapdirec[heady][headx] == 1)//左{if (map[heady][headx - 2] == 1){ broadcast = 1;// 撞墙寄}else if (map[heady][headx - 2] == 2){broadcast = 2;// 撞自己寄}else if (map[heady][headx - 2] == 3){broadcast = 3;//吃到食物辣++length;headx -= 2;map[heady][headx] = 2;mapdirec[heady][headx] = 1;}else if (map[heady][headx - 2] == 4){headx -= 2;map[heady][headx] = 2;mapdirec[heady][headx] = 1;}}else if (mapdirec[heady][headx] == 3)//右{if (map[heady][headx + 2] == 1){broadcast = 1;// 撞墙寄}else if (map[heady][headx + 2] == 2){broadcast = 2;// 撞自己寄}else if (map[heady][headx + 2] == 3){broadcast = 3;//吃到食物辣++length;headx += 2;map[heady][headx] = 2;mapdirec[heady][headx] = 3;}else if (map[heady][headx + 2] == 4){headx += 2;map[heady][headx] = 2;mapdirec[heady][headx] = 3;}}else if (mapdirec[heady][headx] == 2)//上{if (map[heady - 1][headx] == 1){broadcast = 1;// 撞墙寄}else if (map[heady - 1][headx] == 2){broadcast = 2;// 撞自己寄}else if (map[heady - 1][headx] == 3){broadcast = 3;//吃到食物辣++length;heady -= 1;map[heady][headx] = 2;mapdirec[heady][headx] = 2;}else if (map[heady - 1][headx] == 4){heady -= 1;map[heady][headx] = 2;mapdirec[heady][headx] = 2;}}else if (mapdirec[heady][headx] == 4)//下{if (map[heady + 1][headx] == 1){broadcast = 1;// 撞墙寄}else if (map[heady + 1][headx] == 2){broadcast = 2;// 撞自己寄}else if (map[heady + 1][headx] == 3){broadcast = 3;//吃到食物辣++length;heady += 1;map[heady][headx] = 2;mapdirec[heady][headx] = 4;}else if (map[heady + 1][headx] == 4){heady += 1;map[heady][headx] = 2;mapdirec[heady][headx] = 4;}}gotoxy(0, height + 2);printf("                               \r分数:%d  目前最高分:%d",length,maxscore);
}void move()//蛇移动的代码
{gotoxy(headx + 1, heady);printf("\bX");if (mapdirec[heady][headx] == 1){gotoxy(headx + 3, heady);printf("\bO");}else if (mapdirec[heady][headx] == 3){gotoxy(headx - 1, heady);printf("\bO");}else if (mapdirec[heady][headx] == 2){gotoxy(headx + 1, heady + 1);printf("\bO");}else if (mapdirec[heady][headx] == 4){gotoxy(headx + 1, heady - 1);printf("\bO");}if (broadcast == 3){food();if (length == width * height)broadcast = 4;}else {gotoxy(tailx + 1, taily);printf("\b ");map[taily][tailx] = 4;if (mapdirec[taily][tailx] == 1){mapdirec[taily][tailx] = 0;tailx -= 2;}else if (mapdirec[taily][tailx] == 2){mapdirec[taily][tailx] = 0;taily -= 1;}else if (mapdirec[taily][tailx] == 3){mapdirec[taily][tailx] = 0;tailx += 2;}else if (mapdirec[taily][tailx] == 4){mapdirec[taily][tailx] = 0;taily += 1;}}broadcast = 0;
}void game()
{gotoxy(0, height + 2);printf("按下D或S以开始游戏");while (direc == 0){name();int num = 0;Sleep(5);if (_kbhit()){num = _getch();if (num == 100 or num == 68)//d按下direc = 3;if (num == 115 or num == 83)//s按下direc = 4;}}mapdirec[heady][headx] = direc; food();while (1){name();detect();if (broadcast == 1 or broadcast == 2)break;move();if (broadcast == 4)break;getdirec_wait();}
}void over()
{gotoxy(0, height + 2);printf("\r ");if (broadcast == 1){		printf("撞到墙了!!!游戏结束,本次得分为%d",length);		}else if (broadcast == 2){		printf("撞到自己了!!!游戏结束,本次得分为%d",length);		}    else if (broadcast == 4){printf("你也太厉害了叭!!!游戏通关,本次得分为最高分%d", length);}if (length > maxscore)maxscore = length;gotoxy(0, height + 3);printf("按1再来一次\n");int num;scanf("%d", &num);if (num == 1){system("cls");broadcast = 5;}elsebroadcast = 0;
}void ready()
{int num;broadcast = 0;length = 3;headx = length * 2, heady = 1;tailx = 2, taily = 1;direc = 0;printf("请选择游戏难度,慢请选1,正常请选2,真正的挑战请选3\n");scanf("%d", &num);if (num == 1)speed = 20;else if (num == 2)speed = 10;else if (num == 3)speed = 5;
}void main()
{do{ready();HideCursor();initmap();direcmap();drawmap();game();over();	} while (broadcast == 5);
}

希望以后能继续进步,记录自己学习成长的过程吧

更多推荐

入门级c语言贪吃蛇教程

本文发布于:2024-03-04 02:39:44,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1708051.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:入门级   贪吃蛇   语言   教程

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!