1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > C语言C++图形库---贪吃蛇大作战【附源码】

C语言C++图形库---贪吃蛇大作战【附源码】

时间:2018-09-09 08:28:29

相关推荐

C语言C++图形库---贪吃蛇大作战【附源码】

这一节中,我们来做一款经典小游戏,贪吃蛇。先看看最终效果图

在开始之前,我们把窗体创建好。

创建一个800 * 600的窗体。这一次我们使用默认的原点和坐标轴:原点在窗体左上角,X轴正方向向右,Y轴正方向向下。背景色设置为RGB(164, 225, 202),最后调用cleardevice函数,使用背景色清空整个窗体。

#include<easyx.h>#include<stdio.h>intmain(){initgraph(800,600);//设置背景色setbkcolor(RGB(164,225,202));//使用背景色清空窗体cleardevice();getchar();closegraph();return0;}

1. 定位网格

将整个800 * 600的窗体,水平分隔为20等分,垂直分隔为15等分,作为整个游戏的网格坐标系统。在上图中,蛇用5格白色的矩形表示。食物用黄色的一格矩形表示。

这样,蛇的每一格身体坐标为:

(5, 7)

(4, 7)

(3, 7)

(2, 7)

(1, 7)

食物的坐标为:

(12, 7)

为了方便观察,把窗体用线段画上上述网格。网格每一格的宽度设为40像素,用符号常量NODE_WIDTH表示。

#defineNODE_WIDTH40

竖向线段

先绘制竖向的线段。

竖向线段中,起始点y坐标固定为0,终止点y坐标固定为600

每条线段的起始点与终止点的x坐标一致,且随着线段不同而变化。

设线段条数从0开始计数。

第0条线段: 起始点、终止点的x坐标为0

第10条线段: 起始点、终止点的x坐标为10 * NODE_WIDTH

第20条线段: 起始点、终止点的x坐标为20 * NODE_WIDTH

观察各线段起始点终止点坐标,可以总结出规律:

第n条线段:起始点(n * NODE_WIDTH, 0)、终止点(n * NODE_WIDTH, 600)。且x坐标的范围为[0, 800]

//竖线for(intx=0;x<=800;x+=NODE_WIDTH){line(x,0,x,600);}

横向线段

再绘制横向的线段。

横向线段中,起始点x坐标固定为0,终止点x坐标固定为800

每条线段的起始点与终止点的y坐标一致,且随着线段不同而变化。

设线段条数从0开始计数。

第0条线段: 起始点、终止点的y坐标为0

第8条线段: 起始点、终止点的y坐标为8 * NODE_WIDTH

第15条线段: 起始点、终止点的y坐标为15 * NODE_WIDTH

观察各线段起始点终止点坐标,可以总结出规律:

第n条线段:起始点(0, n * NODE_WIDTH)、终止点(800, n * NODE_WIDTH)。且y坐标的范围为[0, 600]

//横线for(inty=0;y<=600;y+=NODE_WIDTH){line(0,y,800,y);}

网格函数paintGrid

将绘制网格的代码封装成函数paintGrid

//绘制网格//横线(0,y),(800,y)0<=y<=600//竖线(x,0),(x,600)0<=x<=800voidpaintGrid(){//横线for(inty=0;y<600;y+=NODE_WIDTH){line(0,y,800,y);}//竖线for(intx=0;x<800;x+=NODE_WIDTH){line(x,0,x,600);}}

主函数中调用paintGrid函数,给整个窗体绘制上网格。

现阶段代码如下:

#include<easyx.h>#include<stdio.h>#defineNODE_WIDTH40//绘制网格//横线(0,y),(800,y)0<=y<=600//竖线(x,0),(x,600)0<=x<=800voidpaintGrid(){//横线for(inty=0;y<600;y+=NODE_WIDTH){line(0,y,800,y);}//竖线for(intx=0;x<800;x+=NODE_WIDTH){line(x,0,x,600);}}intmain(){initgraph(800,600);//设置背景色setbkcolor(RGB(164,225,202));//使用背景色清空窗体cleardevice();//绘制网格paintGrid();getchar();closegraph();return0;}

2. 绘制蛇节点

设定初始状态下,蛇有5个节点。我们把之前设置的网格规划出来的坐标称作网格坐标。每个节点的网格坐标为:

(5, 7)

(4, 7)

(3, 7)

(2, 7)

(1, 7)

每一个蛇节点使用一个白色正方形表示,而绘制矩形需要左上角和右下角的实际坐标。白色矩形的左上角坐标与右下角实际坐标为:

左上角:【网格x坐标 * 网格宽度, 网格y坐标 * 网格宽度】

右下角:【(网格x坐标+ 1) *网格宽度, (网格y坐标+ 1) *网格宽度

每个节点需要存储x坐标、y坐标两个坐标,可以定义含有两个int成员的结构用于表示节点。

//节点typedefstruct{intx;inty;}node;

在主函数中,声明node的数组,并将前5个元素初始化为蛇的初始位置。设节点(5, 7)为蛇头,蛇头存储在数组的首元素中

//蛇节点坐标nodesnake[100]={{5,7},{4,7},{3,7},{2,7},{1,7}};

现在,我们定义一个函数paintSnake用于将所有组成蛇的矩形绘制出来。

voidpaintSnake(node*snake,intn){intleft,top,right,bottom;for(inti=0;i<n;i++){//左上角:【网格x坐标*网格宽度, 网格y坐标*网格宽度】left=snake[i].x*NODE_WIDTH;top=snake[i].y*NODE_WIDTH;//右下角:【(网格x坐标+ 1)*网格宽度, (网格y坐标+ 1)*网格宽度】right=(snake[i].x+1)*NODE_WIDTH;bottom=(snake[i].y+1)*NODE_WIDTH;//通过左上角与右下角坐标绘制矩形solidrectangle(left,top,right,bottom);}}

paintSnake函数需要蛇节点数组首元素指针和蛇节点个数两个参数。在主函数中定义一个变量int length用于记录蛇长度,目前初始化为5。声明蛇节点数组,绘制网格及蛇节点。

//蛇节点坐标

nodesnake[100]={{5,7},{4,7},{3,7},{2,7},{1,7}};

//蛇节点长度

intlength=5;

//绘制网格

paintGrid();

//绘制蛇节点

paintSnake(snake,length);

现阶段代码如下:

#include<easyx.h>#include<stdio.h>#defineNODE_WIDTH40//节点typedefstruct{intx;inty;}node;//绘制网格//横线(0,y),(800,y)0<=y<=600//竖线(x,0),(x,600)0<=x<=800voidpaintGrid(){//横线for(inty=0;y<600;y+=NODE_WIDTH){line(0,y,800,y);}//竖线for(intx=0;x<800;x+=NODE_WIDTH){line(x,0,x,600);}}voidpaintSnake(node*snake,intn){intleft,top,right,bottom;for(inti=0;i<n;i++){//左上角:【网格x坐标*网格宽度, 网格y坐标*网格宽度】left=snake[i].x*NODE_WIDTH;top=snake[i].y*NODE_WIDTH;//右下角:【(网格x坐标+ 1)*网格宽度, (网格y坐标+ 1)*网格宽度】right=(snake[i].x+1)*NODE_WIDTH;bottom=(snake[i].y+1)*NODE_WIDTH;//通过左上角与右下角坐标绘制矩形solidrectangle(left,top,right,bottom);}}intmain(){initgraph(800,600);//设置背景色setbkcolor(RGB(164,225,202));//使用背景色清空窗体cleardevice();//蛇节点坐标nodesnake[100]={{5,7},{4,7},{3,7},{2,7},{1,7}};//蛇节点长度intlength=5;//绘制网格paintGrid();//绘制蛇节点paintSnake(snake,length);getchar();closegraph();return0;}

3. 移动蛇节点

现在,我们希望让蛇向右运动,蛇头节点为(5, 7)。以下两幅图为初始位置向右移动一步后的位置。

初始位置:

(5, 7)

(4, 7)

(3, 7)

(2, 7)

(1, 7)

向右移动一步后位置:

(6, 7)

(5, 7)

(4, 7)

(3, 7)

(2, 7)

可以看出移动后,蛇节点中,尾部节点(1, 7)被删除,而新增了一个头部节点(6,7)

怎样对存储蛇节点的数组进行操作,可以达到删除尾部节点,而新增一个头节点的效果呢?

逐个向后移动

从蛇尾节点前一个节点开始,即从数组下标为3的元素开始。元素3设置为当前元素,将当前元素的值,移动到后一个元素当中。接着,将当前元素设置为元素2,重复该动作,直到数组元素0的值,移动到元素1为止。

元素3-->元素4

元素2-->元素3

元素1-->元素2

元素0-->元素1

这样即可把蛇尾节点去掉,并留出了新蛇头节点的位置。

设置新蛇头

若蛇向右运动,那么将新蛇头设置为(6,7)即可。

但是,蛇头除了可以向右运动,还可以做另外3个方向的运动,共4个方向的运动。

设旧蛇头坐标为(x, y)。对于各个方向的运动,新蛇头的网格坐标相对于旧蛇头的坐标作如下运算:

上:(x, y + 1)

下:(x, y - 1)

左:(x - 1, y)

右:(x + 1, y)

为了更加明确地在程序中表明方向,我们将4个方向声明为枚举类型。

//方向枚举enumdirection{eUp,eDown,eLeft,eRight};

接着定义一个函数snakeMove,传入数组首元素指针、蛇节点个数和蛇前进方向。它将按照上述方法,依次移动蛇节点并根据前进方向设置蛇头。

//蛇节点移动voidsnakeMove(node*snake,intlength,intdirection){//从尾结点开始,前一个节点覆盖后一个节点//4321043210//EDCBA--->DECBAfor(inti=length-1;i>0;i--){snake[i]=snake[i-1];}//根据方向,确定下一个头节点nodenewHead;newHead=snake[0];if(direction==eUp){newHead.y--;}elseif(direction==eDown){newHead.y++;}elseif(direction==eLeft){newHead.x--;}else//right{newHead.x++;}//更新头节点//DECBA--->DECBNsnake[0]=newHead;}

在主函数中放置一个死循环,循环体中依次执行以下步骤:

清空整个窗体

绘制网格

绘制蛇节点

休眠500ms

向右移动蛇节点

while(1)

{

//清空整个窗体

cleardevice();

//绘制网格

paintGrid();

//绘制蛇节点

paintSnake(snake,length);

//休眠500ms

Sleep(500);

//向右移动蛇节点

snakeMove(snake,length,eRight);

}

现阶段代码如下:

#include<easyx.h>#include<stdio.h>#defineNODE_WIDTH40//节点typedefstruct{intx;inty;}node;//绘制网格//横线(0,y),(800,y)0<=y<=600//竖线(x,0),(x,600)0<=x<=800voidpaintGrid(){//横线for(inty=0;y<600;y+=NODE_WIDTH){line(0,y,800,y);}//竖线for(intx=0;x<800;x+=NODE_WIDTH){line(x,0,x,600);}}voidpaintSnake(node*snake,intn){intleft,top,right,bottom;for(inti=0;i<n;i++){//左上角:【网格x坐标*网格宽度, 网格y坐标*网格宽度】left=snake[i].x*NODE_WIDTH;top=snake[i].y*NODE_WIDTH;//右下角:【(网格x坐标+ 1)*网格宽度, (网格y坐标+ 1)*网格宽度】right=(snake[i].x+1)*NODE_WIDTH;bottom=(snake[i].y+1)*NODE_WIDTH;//通过左上角与右下角坐标绘制矩形solidrectangle(left,top,right,bottom);}}//方向枚举enumdirection{eUp,eDown,eLeft,eRight};//蛇节点移动voidsnakeMove(node*snake,intlength,intdirection){//从尾结点开始,前一个节点覆盖后一个节点//4321043210//EDCBA--->DECBAfor(inti=length-1;i>0;i--){snake[i]=snake[i-1];}//根据方向,确定下一个头节点nodenewHead;newHead=snake[0];if(direction==eUp){newHead.y--;}elseif(direction==eDown){newHead.y++;}elseif(direction==eLeft){newHead.x--;}else//right{newHead.x++;}//更新头节点//DECBA--->DECBNsnake[0]=newHead;}intmain(){initgraph(800,600);//设置背景色setbkcolor(RGB(164,225,202));//使用背景色清空窗体cleardevice();//蛇节点坐标nodesnake[100]={{5,7},{4,7},{3,7},{2,7},{1,7}};//蛇节点长度intlength=5;while(1){//清空整个窗体cleardevice();//绘制网格paintGrid();//绘制蛇节点paintSnake(snake,length);//休眠500msSleep(500);//向右移动蛇节点snakeMove(snake,length,eRight);}getchar();closegraph();return0;}

4. 控制移动方向

现在,程序开始后蛇会向右移动,接下来,加入用键盘来控制蛇的移动方向的功能。

键盘交互功能如下:

按下w键,蛇向上移动

按下s键,蛇向下移动

按下a键,蛇向左移动

按下d键,蛇向右移动

在主函数中声明一个枚举变量d,初始为向右移动。

//移动方向enumdirectiond=eRight;

d传递给snakeMove函数的第三个参数。snakeMove函数的第三个参数可以根据方向设置新蛇头的位置。若蛇需要更改移动方向,只要更改枚举变量d即可。

//枚举变量d,控制蛇的移动方向snakeMove(snake,length,d);

与之前的键盘交互一样,使用_getch_kbhit函数配合,可以获取键盘输入且不会导致程序阻塞。使用这两个函数别忘了包含头文件#include <conio.h>。获取到键盘输入后,通过传入枚举变量指针pD,修改变量枚举值。

//键盘输入改变directionvoidchangeDirection(enumdirection*pD){//检查输入缓存区中是否有数据if(_kbhit()!=0){//_getch函数获取输入缓存区中的数据charc=_getch();//判断输入并转向switch(c){case'w'://向上移动*pD=eUp;break;case's'://向下移动*pD=eDown;break;case'a'://向左移动*pD=eLeft;break;case'd'://向右移动*pD=eRight;break;}}}

这里还需要注意一个问题,蛇不能后退,如果新的方向与原方向相反,那么按键无效。

//键盘输入改变directionvoidchangeDirection(enumdirection*pD){//检查输入缓存区中是否有数据if(_kbhit()!=0){//_getch函数获取输入缓存区中的数据charc=_getch();//判断输入并转向switch(c){case'w'://向上移动if(*pD!=eDown)*pD=eUp;break;case's'://向下移动if(*pD!=eUp)*pD=eDown;break;case'a'://向左移动if(*pD!=eRight)*pD=eLeft;break;case'd'://向右移动if(*pD!=eLeft)*pD=eRight;break;}}}

主函数中,在snakeMove函数前调用changeDirection函数检查是否有键盘输入,若有输入且非回头方向的输入,则改变方向枚举变量d。新的方向枚举变量d传入snakeMove函数后,即可使用新方向设置新蛇头的位置,实现蛇改变移动方向功能。

//蛇节点坐标

nodesnake[100]={{5,7},{4,7},{3,7},{2,7},{1,7}};

//蛇节点长度

intlength=5;

enumdirectiond=eRight;

while(1)

{

cleardevice();

paintGrid();

paintSnake(snake,length);

Sleep(500);

//获取键盘输入并将方向存储到变量d

changeDirection(&d);

//根据变量d的方向移动蛇节点

snakeMove(snake,length,d);

}

现阶段代码:

#include<easyx.h>#include<stdio.h>#include<conio.h>#defineNODE_WIDTH40//节点typedefstruct{intx;inty;}node;//绘制网格//横线(0,y),(800,y)0<=y<=600//竖线(x,0),(x,600)0<=x<=800voidpaintGrid(){//横线for(inty=0;y<600;y+=NODE_WIDTH){line(0,y,800,y);}//竖线for(intx=0;x<800;x+=NODE_WIDTH){line(x,0,x,600);}}voidpaintSnake(node*snake,intn){intleft,top,right,bottom;for(inti=0;i<n;i++){//左上角:【网格x坐标*网格宽度, 网格y坐标*网格宽度】left=snake[i].x*NODE_WIDTH;top=snake[i].y*NODE_WIDTH;//右下角:【(网格x坐标+ 1)*网格宽度, (网格y坐标+ 1)*网格宽度】right=(snake[i].x+1)*NODE_WIDTH;bottom=(snake[i].y+1)*NODE_WIDTH;//通过左上角与右下角坐标绘制矩形solidrectangle(left,top,right,bottom);}}//方向枚举enumdirection{eUp,eDown,eLeft,eRight};//蛇节点移动voidsnakeMove(node*snake,intlength,intdirection){//从尾结点开始,前一个节点覆盖后一个节点//4321043210//EDCBA--->DECBAfor(inti=length-1;i>0;i--){snake[i]=snake[i-1];}//根据方向,确定下一个头节点nodenewHead;newHead=snake[0];if(direction==eUp){newHead.y--;}elseif(direction==eDown){newHead.y++;}elseif(direction==eLeft){newHead.x--;}else//right{newHead.x++;}//更新头节点//DECBA--->DECBNsnake[0]=newHead;}//键盘输入改变directionvoidchangeDirection(enumdirection*pD){//检查输入缓存区中是否有数据if(_kbhit()!=0){//_getch函数获取输入缓存区中的数据charc=_getch();//判断输入并转向switch(c){case'w'://向上移动if(*pD!=eDown)*pD=eUp;break;case's'://向下移动if(*pD!=eUp)*pD=eDown;break;case'a'://向左移动if(*pD!=eRight)*pD=eLeft;break;case'd'://向右移动if(*pD!=eLeft)*pD=eRight;break;}}}intmain(){initgraph(800,600);//设置背景色setbkcolor(RGB(164,225,202));//使用背景色清空窗体cleardevice();//蛇节点坐标nodesnake[100]={{5,7},{4,7},{3,7},{2,7},{1,7}};//蛇节点长度intlength=5;enumdirectiond=eRight;while(1){//清空整个窗体cleardevice();//绘制网格paintGrid();//绘制蛇节点paintSnake(snake,length);//休眠500msSleep(500);//获取键盘输入并将方向存储到变量dchangeDirection(&d);//根据变量d的方向移动蛇节点snakeMove(snake,length,d);}getchar();closegraph();return0;}

5. 创建食物

目前蛇的部分已经完成了,还差需要创建食物,让蛇吃到食物后长大。

主函数中声明一个node类型的变量作为食物的节点。

nodefood;

食物的位置是随机生成的,但是有两个要求:

不能生成在窗体外

不能生成在蛇节点上

对于网格坐标来说,宽度有800/NODE_WIDTH,即20格。高度有600 / NODE_WIDTH,即15格。限制食物的x坐标在区间[0, 19]以内,y坐标在区间[0, 14]内。

food.x=rand()%(800/NODE_WIDTH);//区间[0,19]内food.y=rand()%(600/NODE_WIDTH);//区间[0,14]内

对于第二个条件,只能遍历所有蛇节点,检查是否食物与蛇任何一个节点重合了。如果重合,那么重新随机生成一次食物,直到食物与所有蛇节点不重合为止。

while(1){food.x=rand()%(800/NODE_WIDTH);food.y=rand()%(600/NODE_WIDTH);inti;for(i=0;i<length;i++){if(snake[i].x==food.x&&snake[i].y==food.y){break;}}if(i<length)continue;elsebreak;}

将生成食物封装成createFood函数,函数传入两个参数,一个参数为蛇节点数组首元素指针,另一个参数为蛇节点个数。生成完成食物坐标后,返回装有食物坐标的food结构。

//随机创建食物nodecreateFood(node*snake,intlength){nodefood;while(1){food.x=rand()%(800/NODE_WIDTH);food.y=rand()%(600/NODE_WIDTH);inti;for(i=0;i<length;i++){if(snake[i].x==food.x&&snake[i].y==food.y){break;}}if(i<length)continue;elsebreak;}returnfood;}

根据createFood函数返回的食物坐标,在窗体上绘制一个黄色矩形代表食物。将绘制食物的代码封装成函数paintFood

食物的左上角坐标为:【food.x * NODE_WIDTH, food.y * NODE_WIDTH】

食物的右下角坐标为:【(food.x + 1) * NODE_WIDTH, (food.y + 1) * NODE_WIDTH】

//绘制食物voidpaintFood(nodefood){intleft,top,right,bottom;left=food.x*NODE_WIDTH;top=food.y*NODE_WIDTH;right=(food.x+1)*NODE_WIDTH;bottom=(food.y+1)*NODE_WIDTH;setfillcolor(YELLOW);solidrectangle(left,top,right,bottom);setfillcolor(WHITE);}

主函数中,第一次生成食物在循环外。循环内部每次循环绘制一次食物。使用了随机数,别忘了使用当前时间作为随机数种子。另外,time函数需要包含头文件#include <time.h>

//随机生成食物

srand(unsignedint(time(NULL)));

nodefood=createFood(snake,length);

while(1)

{

cleardevice();

paintGrid();

paintSnake(snake,length);

//绘制食物

paintFood(food);

Sleep(500);

changeDirection(&d);

snakeMove(snake,length,d);

}

现阶段代码:

#include<easyx.h>#include<stdio.h>#include<conio.h>#include<time.h>#defineNODE_WIDTH40//节点typedefstruct{intx;inty;}node;//绘制网格//横线(0,y),(800,y)0<=y<=600//竖线(x,0),(x,600)0<=x<=800voidpaintGrid(){//横线for(inty=0;y<600;y+=NODE_WIDTH){line(0,y,800,y);}//竖线for(intx=0;x<800;x+=NODE_WIDTH){line(x,0,x,600);}}voidpaintSnake(node*snake,intn){intleft,top,right,bottom;for(inti=0;i<n;i++){//左上角:【网格x坐标*网格宽度, 网格y坐标*网格宽度】left=snake[i].x*NODE_WIDTH;top=snake[i].y*NODE_WIDTH;//右下角:【(网格x坐标+ 1)*网格宽度, (网格y坐标+ 1)*网格宽度】right=(snake[i].x+1)*NODE_WIDTH;bottom=(snake[i].y+1)*NODE_WIDTH;//通过左上角与右下角坐标绘制矩形solidrectangle(left,top,right,bottom);}}//方向枚举enumdirection{eUp,eDown,eLeft,eRight};//蛇节点移动voidsnakeMove(node*snake,intlength,intdirection){//从尾结点开始,前一个节点覆盖后一个节点//4321043210//EDCBA--->DECBAfor(inti=length-1;i>0;i--){snake[i]=snake[i-1];}//根据方向,确定下一个头节点nodenewHead;newHead=snake[0];if(direction==eUp){newHead.y--;}elseif(direction==eDown){newHead.y++;}elseif(direction==eLeft){newHead.x--;}else//right{newHead.x++;}//更新头节点//DECBA--->DECBNsnake[0]=newHead;}//键盘输入改变directionvoidchangeDirection(enumdirection*pD){//检查输入缓存区中是否有数据if(_kbhit()!=0){//_getch函数获取输入缓存区中的数据charc=_getch();//判断输入并转向switch(c){case'w'://向上移动if(*pD!=eDown)*pD=eUp;break;case's'://向下移动if(*pD!=eUp)*pD=eDown;break;case'a'://向左移动if(*pD!=eRight)*pD=eLeft;break;case'd'://向右移动if(*pD!=eLeft)*pD=eRight;break;}}}//绘制食物/*(x*NODE_WIDTH,y*NODE_WIDTH)@-----------||||||||||-----------@((x+1)*NODE_WIDTH,(y+1)*NODE_WIDTH)*/voidpaintFood(nodefood){intleft,top,right,bottom;left=food.x*NODE_WIDTH;top=food.y*NODE_WIDTH;right=(food.x+1)*NODE_WIDTH;bottom=(food.y+1)*NODE_WIDTH;setfillcolor(YELLOW);solidrectangle(left,top,right,bottom);setfillcolor(WHITE);}//随机创建食物nodecreateFood(node*snake,intlength){nodefood;while(1){food.x=rand()%(800/NODE_WIDTH);food.y=rand()%(600/NODE_WIDTH);inti;for(i=0;i<length;i++){if(snake[i].x==food.x&&snake[i].y==food.y){break;}}if(i<length)continue;elsebreak;}returnfood;}intmain(){initgraph(800,600);//设置背景色setbkcolor(RGB(164,225,202));//使用背景色清空窗体cleardevice();//蛇节点坐标nodesnake[100]={{5,7},{4,7},{3,7},{2,7},{1,7}};//蛇节点长度intlength=5;enumdirectiond=eRight;//食物srand(unsignedint(time(NULL)));nodefood=createFood(snake,length);while(1){//清空整个窗体cleardevice();//绘制网格paintGrid();//绘制蛇节点paintSnake(snake,length);//绘制食物paintFood(food);//休眠500msSleep(500);//获取键盘输入并将方向存储到变量dchangeDirection(&d);//根据变量d的方向移动蛇节点snakeMove(snake,length,d);}getchar();closegraph();return0;}

6. 吃掉食物并长大

观察下图,蛇头为节点(11, 7)。而蛇目前向右运动,下一步即将吃到在(12, 7)位置的食物。

现在蛇头移动到了(12, 7)的位置,并吃掉了食物。原蛇尾节点(6, 7)本应该删除,但是这时蛇吃掉了食物,需要长大一节。再将蛇尾节点(6, 7)加回来。

为了获得被删除的原蛇尾节点,snakeMove函数需要返回原蛇尾节点,函数返回值从void改为node。函数中需要记录原蛇尾节点,并在最后返回原蛇尾节点。

//蛇身体移动nodesnakeMove(node*snake,intlength,intdirection){//记录尾节点nodetail=snake[length-1];for(inti=length-1;i>0;i--){snake[i]=snake[i-1];}nodenewHead;newHead=snake[0];if(direction==eUp){newHead.y--;}elseif(direction==eDown){newHead.y++;}elseif(direction==eLeft){newHead.x--;}else{newHead.x++;}snake[0]=newHead;//返回尾节点returntail;}

在主函数的循环中,作新的蛇头节点是否与食物节点重合的判断。若蛇头节点与食物节点重合,那么蛇会长大一节。将snakeMove函数返回的上一次尾结点添加到蛇尾后面,蛇节点长度加1。并且,食物被吃掉以后,应当重新生成新的食物,作为新的目标。

注意蛇不能无限生长而超过snake数组的长度,当length大于等于100时,吃到食物就不再增加长度了。

while(1)

{

cleardevice();

paintGrid();

paintSnake(snake,length);

paintFood(food);

Sleep(500);

changeDirection(&d);

nodelastTail=snakeMove(snake,length,d);

//新的蛇头节点是否与食物节点重合

if(snake[0].x==food.x&&snake[0].y==food.y)

{

//限制snake节点最大长度

if(length<100)

{

//已经吃到食物,长度+1

snake[length]=lastTail;

length++;

}

//重新生成新的食物

food=createFood(snake,length);

}

}

现阶段代码:

#include<easyx.h>#include<stdio.h>#include<conio.h>#include<time.h>#defineNODE_WIDTH40//节点typedefstruct{intx;inty;}node;//绘制网格//横线(0,y),(800,y)0<=y<=600//竖线(x,0),(x,600)0<=x<=800voidpaintGrid(){//横线for(inty=0;y<600;y+=NODE_WIDTH){line(0,y,800,y);}//竖线for(intx=0;x<800;x+=NODE_WIDTH){line(x,0,x,600);}}voidpaintSnake(node*snake,intn){intleft,top,right,bottom;for(inti=0;i<n;i++){//左上角:【网格x坐标*网格宽度, 网格y坐标*网格宽度】left=snake[i].x*NODE_WIDTH;top=snake[i].y*NODE_WIDTH;//右下角:【(网格x坐标+ 1)*网格宽度, (网格y坐标+ 1)*网格宽度】right=(snake[i].x+1)*NODE_WIDTH;bottom=(snake[i].y+1)*NODE_WIDTH;//通过左上角与右下角坐标绘制矩形solidrectangle(left,top,right,bottom);}}//方向枚举enumdirection{eUp,eDown,eLeft,eRight};//蛇身体移动nodesnakeMove(node*snake,intlength,intdirection){//for(inti=0;i<length;i++)//printf("(%d,%d)\n",snake[i].x,snake[i].y);//记录尾节点nodetail=snake[length-1];//从尾结点开始,前一个节点覆盖后一个节点//0123401234//EDCBA--->EEDCBfor(inti=length-1;i>0;i--){snake[i]=snake[i-1];}//下一个头节点nodenewHead;newHead=snake[0];if(direction==eUp){newHead.y--;}elseif(direction==eDown){newHead.y++;}elseif(direction==eLeft){newHead.x--;}else//right{newHead.x++;}//更新头节点//EDCBA--->FEDCBsnake[0]=newHead;//for(inti=0;i<length;i++)//printf("(%d,%d)\n",snake[i].x,snake[i].y);//返回尾节点returntail;}//键盘输入改变directionvoidchangeDirection(enumdirection*pD){//检查输入缓存区中是否有数据if(_kbhit()!=0){//_getch函数获取输入缓存区中的数据charc=_getch();//判断输入并转向switch(c){case'w'://向上移动if(*pD!=eDown)*pD=eUp;break;case's'://向下移动if(*pD!=eUp)*pD=eDown;break;case'a'://向左移动if(*pD!=eRight)*pD=eLeft;break;case'd'://向右移动if(*pD!=eLeft)*pD=eRight;break;}}}//绘制食物/*(x*NODE_WIDTH,y*NODE_WIDTH)@-----------||||||||||-----------@((x+1)*NODE_WIDTH,(y+1)*NODE_WIDTH)*/voidpaintFood(nodefood){intleft,top,right,bottom;left=food.x*NODE_WIDTH;top=food.y*NODE_WIDTH;right=(food.x+1)*NODE_WIDTH;bottom=(food.y+1)*NODE_WIDTH;setfillcolor(YELLOW);solidrectangle(left,top,right,bottom);setfillcolor(WHITE);}//随机创建食物nodecreateFood(node*snake,intlength){nodefood;while(1){food.x=rand()%(800/NODE_WIDTH);food.y=rand()%(600/NODE_WIDTH);inti;for(i=0;i<length;i++){if(snake[i].x==food.x&&snake[i].y==food.y){break;}}if(i<length)continue;elsebreak;}returnfood;}intmain(){initgraph(800,600);//设置背景色setbkcolor(RGB(164,225,202));//使用背景色清空窗体cleardevice();//蛇节点坐标nodesnake[100]={{5,7},{4,7},{3,7},{2,7},{1,7}};//蛇节点长度intlength=5;enumdirectiond=eRight;//食物srand(unsignedint(time(NULL)));nodefood=createFood(snake,length);while(1){//清空整个窗体cleardevice();//绘制网格paintGrid();//绘制蛇节点paintSnake(snake,length);//绘制食物paintFood(food);//休眠500msSleep(500);//获取键盘输入并将方向存储到变量dchangeDirection(&d);nodelastTail=snakeMove(snake,length,d);//新的蛇头节点是否与食物节点重合if(snake[0].x==food.x&&snake[0].y==food.y){//限制snake节点最大长度if(length<100){//已经吃到食物,长度+1snake[length]=lastTail;length++;}//重新生成新的食物food=createFood(snake,length);}}getchar();closegraph();return0;}

7.游戏结束

最后,我们需要判断游戏是否结束。游戏结束的条件为:

蛇头吃到墙壁

蛇头吃到蛇身

如果满足以上两个条件,则游戏结束,并复位所有设置,重新开始游戏。

游戏网格x坐标区间为[0, 800 / NODE_WIDTH),即[0, 20)

游戏网格y坐标区间为[0, 600 / NODE_WIDTH),即[0, 19)

蛇头snake[0].x小于0或大于等于20,蛇头即吃到左边或右边的墙壁。

蛇头snake[0].y小于0或大于等于15,蛇头即吃到上边或下边的墙壁。

遍历除了蛇头外的所有蛇节点坐标,若有节点坐标与蛇头坐标一致,则表明蛇头吃到了蛇身。

将上面两个结束条件封装成isGameOver函数,若游戏结束则返回true,否则返回false

boolisGameOver(node*snake,intlength){//是否撞墙if(snake[0].x<0||snake[0].x>800/NODE_WIDTH)returntrue;if(snake[0].y<0||snake[0].y>600/NODE_WIDTH)returntrue;//是否吃到蛇身for(inti=1;i<length;i++){if(snake[0].x==snake[i].x&&snake[0].y==snake[i].y)returntrue;}returnfalse;}

游戏结束后,需要调用reset函数,复位蛇节点坐标,蛇节点长度以及前进方向。

voidreset(node*snake,int*pLength,enumdirection*d){snake[0]=node{5,7};snake[1]=node{4,7};snake[2]=node{3,7};snake[3]=node{2,7};snake[4]=node{1,7};*pLength=5;*d=eRight;}

在主函数的循环中,添加游戏结束的判断。若游戏结束,复位各种设置。由于,蛇身坐标被复位,有可能与之前的食物坐标重合。因此,也应当重新生成食物。

完整源码请加群【881577770】获取!里面有一些资料可以帮助大家更好的学习,在学习C语言的过程中遇到任何的问题,都可以发出来一起讨论,大家都是学习C/C++的,或是转行,或是大学生,还有工作中想提升自己能力的前端党,如果你是正在学习C/C++的小伙伴可以加入学习。

while(1){cleardevice();paintGrid();paintSnake(snake,length);paintFood(food);Sleep(500);changeDirection(&d);nodelastTail=snakeMove(snake,length,d);if(snake[0].x==food.x&&snake[0].y==food.y){if(length<100){snake[length]=lastTail;length++;}food=createFood(snake,length);}//游戏是否结束if(isGameOver(snake,length)==true){//游戏结束,复位设置,重新生成食物reset(snake,&length,&d);food=createFood(snake,length);}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。