贪吃蛇设计思路.docx

上传人:b****6 文档编号:6056567 上传时间:2023-01-03 格式:DOCX 页数:13 大小:35.17KB
下载 相关 举报
贪吃蛇设计思路.docx_第1页
第1页 / 共13页
贪吃蛇设计思路.docx_第2页
第2页 / 共13页
贪吃蛇设计思路.docx_第3页
第3页 / 共13页
贪吃蛇设计思路.docx_第4页
第4页 / 共13页
贪吃蛇设计思路.docx_第5页
第5页 / 共13页
点击查看更多>>
下载资源
资源描述

贪吃蛇设计思路.docx

《贪吃蛇设计思路.docx》由会员分享,可在线阅读,更多相关《贪吃蛇设计思路.docx(13页珍藏版)》请在冰豆网上搜索。

贪吃蛇设计思路.docx

贪吃蛇设计思路

《贪吃蛇》游戏设计思路

我前一段时间写了个贪吃蛇,有位吧友求思路,而网上也没有类似的教程帖子(至少我当初没找到(-_-),我只找到了现成的代码……),于是乎,我就整理了一下自己的设计思路,写下了这篇东西,如果你是老鸟,有什么说错的地方还希望您具体指出来,毕竟我的功力还不深。

如果你是新手,大家一起交流探讨吧,我也算是刚入门的新手。

在本帖最后,我会附上自己写的完整的程序代码,里面也有一些注释,以供大家参考。

首先,看看我的贪吃蛇的程序框图。

从图中可以看出,整个游戏就是一个大的循环,当判断蛇的生命值为0时就跳出循环游戏结束,否则继续游戏。

常用的结构是:

While

(1){

..........

//游戏内容

..........

If(……)break;

//满足游戏结束的条件时跳出循环结束游戏

}

解决了游戏的主体结构,接下来就是游戏的具体内容了。

先来看看我们需要哪些变量;

蛇的身体(链表,包含了蛇每一节的坐标,和生命值);

食物(数组,两个元素,包含了食物的坐标);

蛇的运动方向(整型,共四个值);

首先,蛇的身体应该选用什么数据类型来存储呢?

数组组是肯定不行的,因为数组的大小是固定的,你不知道你的蛇最终能达到多长(或许有高手能让蛇长到占满屏幕?

),而且在数组的头部插入数据是很麻烦的一件事,你必须把所有的数据都往后移动一个元素才行。

因此,此处我选用了链表来储存蛇的身体信息,(有的小伙伴要问了:

什么是链表啊?

这个请自行XX,我的个人看法是,链表就是对结构体的一种应用。

话说我们学了一学期的C语言,老师也没讲链表之类的东西,不知道很正常,它应该属于数据结构这一门学科的内容吧。

如果你不懂链表也没关系,数组完全可以代替它,只是在资源的利用上要浪费一点,在数据的插入与删除上要麻烦一点而已)。

链表的一个节点应该包含哪些内容呢?

首先是蛇的坐标x和y,然后是生命值(其实只有第一个节点能用上它,你也可以去掉它,然后另外单独定义一个全局变量如life来判断蛇的生死),最后是指向下一个节点的结构体指针。

定义如下:

structsnk{//蛇身体

intx;

inty;

intlife;

structsnk*link;

};

然后是食物:

intfood[2];//食物,用food[0]表示x坐标,用food[1]表示y坐标

当然,食物必须定义为全局的。

当然如果你有需要,还可以定义一个全局整型变量来保存积分:

intfen=0;//积分

需要的主要数据已经定义好,接下来是初始化。

那么,就先来初始化蛇的身体,创建一个链表;

structsnk*snake;//指向蛇链表头(第一个节点)的结构体指针

//声明两个个链表节点,初始化最开始的几节蛇身体

snake=(structsnk*)malloc(sizeof(structsnk));

snake->x=10;snake->y=10;snake->life=1;

snake->link=(structsnk*)malloc(sizeof(structsnk));

snake->link->x=10;snake->link->y=9;

snake->link->x=10;snake->link->link=NULL;

这里我只初始化了两节,你也可以多加几节,但是,最后别忘了把最后一个节点的link指针设为空,它标志着蛇的结尾,至于life,只需把第一个节点初始化为非零值即可。

接下来是初始化食物,为了能够随机生成食物,我们需要用到time.h为我们提供的随机函数,rand()和srand();为了方便后面的食物生成,我们把食物生成单独做成一个函数mkfood(),想一想,它需要哪些参数,它的返回值是什么?

为了防止生成的食物的坐标与蛇的身体重合,所以mkfood函数需要知道蛇的状态,食物是全局变量,因此函数不需要返回值,因此mkfood函数的函数首部应该是

voidmkfood(structsnk*p);//产生食物

每次调用它都需要用srand函数重设随机数种子,然后用rand函数产生随机数,分别赋值给food[0]和food[1]作为食物的x坐标和y坐标。

在这之后,需要遍历一次蛇的每一个节点,并把它们与食物的坐标比较,如有相同的,既产生的食物和蛇的身体重复了,就必须重新设定食物的坐标,这里我用的是递归调用mkfood函数来重新设定食物坐标。

如果你用递归方法来重设食物坐标,不得不提的是当你在设定随机数种子时不要使用time()函数,因为time函数只能精确到秒,一旦食物与蛇身体重复,就会递归调用mkfood函数,此时时间还远远不足一秒,所以导致随机数种子还是和原来的一样,产生的随机数也和原来一样,还是和蛇身体重复,如此循环往复的递归调用,以现在的CPU运算能力,一秒钟之内可以进行的运算次数是相当惊人的,最终导致栈的溢出,程序崩溃。

所以应该选用更加精确的函数,比如clock()它返回从程序启动,到调用它时经过的时间,单位是毫秒,或者干脆手动设定一个变量来做种子,每次调用之前更新这个变量。

接下来就是制作一个函数,能够按照蛇身体和食物的信息,把蛇的身体和食物打印(绘制)到屏幕的相应位置。

这个函数需要知道蛇和食物的坐标,食物是全局变量可以直接获取坐标,因此它接受一个结构体指针类型的参数,就是蛇身体的第一个节点,而它不需要返回值。

所以他的函数首部应该是:

voiddrawmap(structsnk*p,int*fd);//画出食物和蛇

至于函数内部,我们可以通过p->x或者p->y来访问蛇的头部的坐标,然后令p=p->link;来继续访问接下来一节蛇的坐标,直到p->link=NULL,这说明蛇到了结尾了。

食物是全局变量可以直接获取坐标。

获得坐标之后,你可以调用图形库来绘制,也可以在字符界面打印。

总之就是遍历一次蛇的身体,根据每一个节点的坐标,分别把它们和食物一起打印在屏幕上就是了。

接下来是蛇的移动问题,怎样让蛇自动的移动呢?

这个只需要主循环while

(1){………}每循环一次就改变一次蛇的位置即可,那么如何改变呢?

这就体现出我们为什么要用链表来储存蛇的坐标了。

我们并不需要改变蛇每一节的坐标,只需要“添头去尾”即可移动蛇,我们只需要在蛇的头部,根据蛇的运动方向添加一个节点,把它设为蛇头,判断此节点的坐标是否超出地图范围,如果超出这修改生命值为死亡,如果没有超出,则继续把它与食物的坐标进行比较,如果不同,则删除蛇的最后一个节点,蛇就向前移动一格了,如果相同,则蛇吃到了食物,此时不删除最后一节蛇的身体,蛇就增加了一节,然后再调用mkfood函数重新生成食物覆盖掉原来食物的坐标。

此处最主要的就是链表的添头去尾操作,添头其实就是新分配一块内存,把它的link指针指向原来的第一个节点,再把现在的第一个节点的地址返回给代表蛇头的结构体指针变量(把新添加的节点设为蛇头),去尾就是把最后一个节点用free()释放掉,把倒数第二个节点的link设为NULL即可。

还有就是如何确定添加的头的坐标的问题,你需要根据之前的头的坐标和代表方向的变量的值来做,比如用1234分别代表上下左右方向,当方向为1时,蛇向上运动,那么在原来的蛇头的基础上把x减1,y不变来作为新头的坐标。

其他方向同理。

然后是蛇的控制问题,

我们,可以用一个函数来获取输入,改变蛇的前进方向;它需要接收当前的运动方向(整型值),用来判断用户按下的方向是否与其相反(蛇不能向与当前方向相反的方向前进),然后返回一个运动方向,所以他的函数首部应该是:

intkeydown(intz)//获取输入

这个函数里,我们可以使用conio.h里的getch来获取键盘输入,为什么不用getchar或者scanf呢?

因为getch与getchar()虽然基本功能差不多,差别是getch()直接从键盘获取键值,不等待用户按回车,只要用户按一个键,getch就立刻返回,而getchar则是从输入缓冲区获取键值,getch返回值是用户输入的ASCII码,出错返回-1.输入的字符不会回显在屏幕上。

但是还有一个问题,那就是当程序执行到getch时会暂停,等待用户输入。

这样如果我们不按键盘,游戏就无法继续。

这个可以用conio.h里提供的kbhit()函数来检测是否有键盘被按下,

函数名:

kbhit()(VC++6.0下为_kbhit())

功能及返回值:

检查当前是否有键盘输入,若有则返回一个非0值,否则返回0

用法:

intkbhit(void);

包含头文件:

include

于是我们可以这样写:

charch;

if(kbhit()){

ch=getch();

switch(ch){

case'a':

;break;

case'd':

;break;

case'w':

;break;

case's':

;break;

default:

break;

}

}

根据按的键的不同来修改蛇的运动方向。

注意哦!

在改变运动方向时不要忘记判断方向是否与当前的方向冲突!

我们还可以用它来判断按下的是否是Esc键,如果是,则结束游戏。

至此,我们已经完成了贪吃蛇的最主要两个数据:

Food[2]和snake{

Intx;

Inty;

Intlife;

Structsnake*link;

}

几个主要的功能函数,

voiddrawmap(structsnk*p,structfood*fd);//画地图食物和蛇

intkeydown(intz);//获取输入

structsnk*mvsnk(structsnk*p,intz,structfood*fd);//移动并更新蛇的坐标

structfood*mkfood(structsnk*p);//产生食物

接下来就是将它们按照开始的程序框图组合起来,一条新鲜的贪吃蛇就出炉了!

当然,你还可以在最外面再套一个大循环以实现多次游戏。

如果你没有ege图形库,只需要稍微更改几个绘制图形的函数,就可以在控制台的字符界面实现了。

你也可以下载这里的编译完成的exe,和我修改的字符界面版:

Exe文件:

字符版:

Ege图形库版:

(相比下面的代码,增加了菜单,即exe的源代码)

以下是完整代码,其中使用了ege图形库,除了文中提到的,我还增加了保存最高纪录的功能。

//|---------------------------------------------------------|

#include

#include

#include

#include

voidmsgbox(intx,inty,intl,inth,char*c,char*c2,char*c3);

voiddrd(intx,inty);//画点

voiddrl(intx1,inty1,intx2,inty2);//画线

voiddrawmap(structsnk*p);//里用上面两个函数画地图食物和蛇

intkeydown(intz);//获取输入

structsnk*mvsnk(structsnk*p,intz);//更新蛇的坐标

voidmkfood(structsnk*p);//产生食物

intfood[2]={20,15};

structsnk{//蛇身体

intx;

inty;

intlife;

structsnk*link;

};

intspeed=100;

intfen=0;//积分

intjilu;//最高分记录

charjiluzhe[20];

intmain()

{

//初始化,

FILE*fp;//从文件载入最高记录

if((fp=fopen("jiulu.txt","r"))==NULL)jilu=0;

else{fgets(jiluzhe,20,fp);

fscanf(fp,"%d",&jilu);}

fclose(fp);

initgraph(600,600);//定义屏幕为600*600像素

setcaption("贪吃蛇");

setcolor(WHITE);

setfont(20,0,"宋体");

intz=4;

structsnk*snake;//指向蛇链表头的结构体指针

structsnk*snakelink;//用于游戏结束后释放内存时的临时存储

structfood*fd;//指向食物的结构体指针

//声明四个链表节点为初始的四节蛇身体

snake=(structsnk*)malloc(sizeof(structsnk));

snake->x=10;snake->y=10;snake->life=1;

snake->link=(structsnk*)malloc(sizeof(structsnk));

snake->link->x=10;snake->link->y=9;

snake->link->link=(structsnk*)malloc(sizeof(structsnk));

snake->link->link->x=10;snake->link->link->y=8;

snake->link->link->link=(structsnk*)malloc(sizeof(structsnk));

snake->link->link->link->x=10;snake->link->link->link->y=7;

snake->link->link->link->link=NULL;

setfontbkcolor(BLUE);

setfont(30,0,"宋体");

xyprintf(0,300,"W:

上S:

下A:

左D:

右Esc:

结束游戏退出");

setfont(20,0,"宋体");

Sleep(2300);

//游戏主循环-----------------------------------------|

while

(1){

inttemp_z;temp_z=z;

z=keydown(z);

if(z==27)break;

if(z==5){

outtextxy(300,300,"暂停");

getch();z=temp_z;}/*|*/

snake=mvsnk(snake,z);

if(snake->life==0)break;

drawmap(snake);

Sleep(300-speed);

}

setfont(50,0,"宋体");

setcolor(YELLOW);

setfontbkcolor(BLUE);

outtextxy(175,250,"GAMEOVER!

");

setcolor(WHITE);

Sleep(1500);

if(fen>jilu){

inputbox_getline("恭喜!

打破纪录了!

","您打破了最高记录!

\n留下您的大名吧!

\n按回车确认输入",jiluzhe,20);

fp=fopen("jiulu.txt","w");

fprintf(fp,"%s\n%d",jiluzhe,fen);

fclose(fp);

Sleep(1500);

}

while(snake!

=NULL){

snakelink=snake->link;

free(snake);

snake=snakelink;

}

fen=0;speed=100;

closegraph();

return0;

}

//----------------------------------------------------|

intkeydown(intz)//获取输入

{

charch;

if(kbhit()){

ch=getch();

switch(ch){

case'A':

case'a':

if(z!

=2)z=1;break;

case'D':

case'd':

if(z!

=1)z=2;break;

case'W':

case'w':

if(z!

=4)z=3;break;

case'S':

case's':

if(z!

=3)z=4;break;

case'E':

case'e':

z=5;break;

default:

break;

}

if(ch==27)z=27;

}

returnz;

}

voidmsgbox(intx,inty,intl,inth,char*c,char*c2,char*c3)

{

setfillcolor(LIGHTGRAY);

bar(x,y,x+l,y+h);

setfillcolor(WHITE);

bar(x+3,y+3,x+l-3,y+h-3);

setfillcolor(BLUE);

bar(x+3,y+3,x+l-3,y+30);

setfontbkcolor(WHITE);

setcolor(BLACK);

rectprintf(x+5,y+3,l-5,h-3,"%s%s%s",c,c2,c3);

setfontbkcolor(BLUE);

setcolor(WHITE);

}

 

structsnk*mvsnk(structsnk*p,intz)//更新蛇的坐标

{

structsnk*p2;

structsnk*p3;

intx,y;

x=p->x;

y=p->y;

switch(z){

case1:

x--;break;

case2:

x++;break;

case3:

y--;break;

case4:

y++;break;

}

//在蛇的头部添加一个节点

p2=(structsnk*)malloc(sizeof(structsnk));

p2->x=x;p2->y=y;

p2->link=p;

p2->life=1;

//判断蛇头是否碰到身体

p3=p;

while(p3!

=NULL){

if(p2->x==p3->x&&p2->y==p3->y)p2->life=0;

p3=p3->link;

}

//判断蛇头是否碰到墙壁

if(p2->x>28||p2->y>28||p2->x<1||p2->y<3)p2->life=0;

if(p2->x==food[0]&&p2->y==food[1]){

p2->life=2;fen++;speed<250?

speed+=2:

speed=250;

mkfood(p2);/*如果吃到食物则不删除最后一个节点,由于之前在头部增加了一个节点,所以蛇的长度增加一节*/

}else{while(p->link->link!

=NULL)p=p->link;

p3=p->link;

p->link=NULL;

free(p3);}//如果没有吃到食物则删除最后一个节点

returnp2;

}

voidmkfood(structsnk*p)

{

structsnk*p2;

p2=p;

srand((unsigned)clock());//设定随机数种子

food[1]=rand()%26+3;//随机产生食物坐标

food[0]=rand()%27+1;

//检测食物的坐标是否与蛇身体重复,如果是,则重新生成食物

do{

if(food[0]==p->x&&food[1]==p->y){mkfood(p2);}

p=p->link;

}while(p!

=NULL);

}

 

voiddrd(intx,inty)//画点

{

setfillcolor(GREEN);

x=x*20;

y=y*20;

bar(x+1,y+1,x+19,y+19);

}

voiddrl(intx1,inty1,intx2,inty2)//画线

{

intx,y;

setfillcolor(GREEN);

do{

x=x1*20;

y=y1*20;

bar(x+1,y+1,x+19,y+19);

x1

x1++:

x2;

y1

y1++:

y2;

}while(x1!

=x2||y1!

=y2);

}

 

voiddrawmap(structsnk*p)//画地图食物和蛇

{

cleardevice();

drl(0,0,30,0);

drl(0,2,30,2);

xyprintf(20,20,"分数:

%d速度:

%d最高记录:

%d保持者:

%s",fen,speed,jilu,jiluzhe);

drl(0,1,0,30);

drl(0,29,30,29);

drl(29,0,29,30);

drd(p->x,p->y);

drd(food[0],food[1]);

p=p->link;

while(p!

=NULL){

drd(p->x,p->y);

p=p->link;

}

}

 

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 自然科学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1