Linux下基于SDL图形库的贪吃蛇游戏.docx
- 文档编号:30006734
- 上传时间:2023-08-04
- 格式:DOCX
- 页数:16
- 大小:80.57KB
Linux下基于SDL图形库的贪吃蛇游戏.docx
《Linux下基于SDL图形库的贪吃蛇游戏.docx》由会员分享,可在线阅读,更多相关《Linux下基于SDL图形库的贪吃蛇游戏.docx(16页珍藏版)》请在冰豆网上搜索。
Linux下基于SDL图形库的贪吃蛇游戏
中文论文题目:
在LINUX平台下实现贪吃蛇
英文论文题目:
GameofSnakeunderLinux
姓名及手机号:
学号:
指导教师:
在LINUX平台下实现贪吃蛇
摘要:
本文主要展示了在LINUX平台下实现贪吃蛇。
主要应用了双向链表、二维结构数组等数据结构,采用了SDL图形库。
建模、设计算法完毕之后,在Linux下进行了运行和调试。
关键词:
贪吃蛇链表苹果SDL
GameofSnakeunderLinux
Abstract:
ThispapermainlyshowsthegameofsnakeunderLinux.Themaindatastructuresaredoublelinkedlistandtwo-dimensionalarray,andIusedtheSDLtoprintthesnakeandapple.Aftermodelingandcompletedthecodes,IrunthegameunderLinux.
KeyWords:
snakelinked-listappleSDL
1.引言
贪吃蛇是一款家喻户晓的小游戏,受到众多青少年的喜爱,在手机上,在电脑上,我们都可以方便的下载到这款小游戏。
而在linux系统下,一样可以利用GNUgcc编译器,使用C语言编程,调用linux自带的图形库函数来完成贪吃蛇的制作。
我在制作本程序时,没有参考任何相关代码,完全独立设计。
2.建模与算法
问题一、贪吃蛇的表示
注意到贪吃蛇是一条蛇,而且它的身体的长度是一节一节的,每一节身体都应当存储坐标与其他数据,显然我应当使用一个链表这个数据结构来处理贪吃蛇的主要程序结构。
链表结构如下图所示:
NILdata1nextprevdata2nextprevdata3NIL
为了起到更好的遍历和数据读取的效果,我采取了双向链表的方式,允许让后一结点方便快捷地获取前
结点的相关信息。
头的prev指针和尾的next指针被设置为NIL。
初始的蛇结构仅有两段身体,即头和尾。
其中,这个双向链表的data数据有三个,一个是当前方块在对应可活动区域的的相对坐标x,另一个是坐标y,有了这个x和y,当此身体节点方块移动以后,我们可以根据其x和y的值画出相应的蛇结构。
另外一个数据储存了prev结点在之前移动过的方向,这样,当蛇头朝某个方向前进一步时,后面的身体结点就可以根据前一结点移动过的方向来移动自己。
有了这三个数据,我就可以呈现整条蛇头改变方向,后面的身体移动到prev结点的位置的动态效果了。
问题二、墙体和某方块已被占据的表示
这是贪吃蛇结构的又一个难点,由于双向链表仅仅表示了蛇的身体结构,仅仅通过蛇的坐标表示,程序是无法获取四周的墙壁信息以及是否撞到了自己的身体这些信息的。
由于整个蛇体可活动区域被分成了块状区域,蛇的着色和移动都是方块化的,那么很容易就可以分析出来整个区域类似于一个表格,可以用一个二维数组来定义每个方块是否被蛇或者墙占据。
如果某个方块在二维数组上对应的值是0,那么说明当前方块为空,贪吃蛇可以移动到此方块上来;如果对应的值是1,那么说明当前方块是墙壁或者是贪吃蛇自己的身体,若蛇头移动到了这个位置则游戏失败。
问题三、苹果的设置
众所周知,贪吃蛇通过吃苹果来增加自己的身体长度,那么就会产生一些问题。
首先是苹果如何放置,因为我们已经通过二维数组将蛇的可活动区域划分成了表格的形状,因此我们可以调用随机函数,在可活动区域中随机选择一个位置来放置苹果。
但是如果这样放置的话,可能会出现苹果放在了蛇的身体位置,或者放在了墙上。
因此,需要进行一个循环判定,如果数组的值是1说明不可放置苹果,重新获取随机数,直到选择了可放置的位置。
但是,当蛇头遇到苹果的时候,如何判定贪吃蛇吃到了呢?
这个问题有多种解决方案:
(1)可以令苹果的坐标参数为全局变量,然后当蛇头移动时,在某个函数里判断蛇头的相对坐标和苹果的坐标是否相等,如果相等则可以扩张链表。
(2)可以将二维数组中的每个元素都设置多个数据,即定义一个结构,多设置一个
变量,该变量默认的值为0,当有苹果放在二维数组的对应位置时,该值更改为1;当苹果被贪吃蛇吃掉的时候,更改为0。
(3)不改变二维数组的数据结构,但是把二维数组里苹果对应的坐标的值更改为2,即它不是墙也不是可移动区域,而是苹果,当蛇头移动到该位置时候,若发现对应二维数组的值是2,则吃到了苹果。
在本程序里,我选择了第二种方案,将二维数组定义为二维结构数组。
问题四、算法设计
数据结构设计完毕,我们就开始了算法的设计。
本程序中,需要实现的功能有:
游戏界面的设计、贪吃蛇身体结构的展现、苹果的设置、贪吃蛇的移动、移动速度的更改等。
(1)游戏界面的设计
由于是在linux下完成,因此我将会用到linux的图形库函数,来完成贪吃蛇可活动区域的建立、游戏名称、分数界面和一些提示的展现。
首先,我选择的视频模式是640x480x16位色,然后,我在屏幕上选择了一块400x400的区域用来当做贪吃蛇的可活动区域。
在这块区域的边界处,我设置了一个矩形框,用来提醒玩家这里是墙,不可碰撞。
这400x400的区域被1600个小矩形所分割,每个矩形的大小为40x40,以横向为x轴,纵向为y轴,就可以看成一个40x40的二维数组,每个数组对应位置的坐标都是0。
但是由于在可活动区域外还有一堵墙存在,因此二维数组需要被设置为42x42,最外围一圈的标记值为1。
而在剩下的空白处,我显示了一些标题文字、按键提醒文字。
整个游戏界面就初步建立了。
(2)贪吃蛇身体结构的展现
在数据结构的建立中,我将贪吃蛇设置为一个双向链表,而双向链表里存储的数据,就是贪吃蛇的身体在40x40的区域内,每个结点的相对坐标,范围为(0,0)到(40,40),初始的蛇只有头尾,其坐标分别为(20,20)和(19,20),只要将这两个点转化为绝对坐标,把队应的10*10的小矩形区域修改成一定的颜色,就可以画出一条蛇。
当蛇的身体移动或者变长的时候,每个身体节点的对应的坐标值也相应改变,再重新打印这条蛇即可。
(3)苹果的设置
前文已经提到,只需要通过rand函数,获取两个0-40的随机数的值,分别对应苹果的x坐标和y坐标,并通过一定的循环避免苹果设置在蛇的身体上或者墙上即可。
(4)贪吃蛇的移动
在程序中,我用一个snake*move()函数来实现蛇的移动,而这个功能的实现也是整个程序的核心。
在双向链表数据结构中,我定义了一个重要的变量Dir来表示本方块的移动方向。
比如程序从键盘中断读入RIGHT按键之后,贪吃蛇向右移动一步,那么Dir的值就会被赋为RIGHT,其中RIGHT是键盘上向右键的ACSII码值,已经过宏定义。
之后,当蛇头移动一步之后,蛇身除蛇头以外的方块都会读取prev结点的Dir值,并移动到prev结点的位置。
对于蛇头来说,有了这个Dir变量之后,在键盘没有输入任何按键时,它会读取自己的Dir的值并作出相应的移动,相当于不更改方向,直线前进,知道键盘输入了某个方向,它才会作出相应动作并改变Dir。
贪吃蛇的移动同样有许多方法。
更加简便的一种是只需要将蛇的最后一个结点移动到蛇头的前一个结点,并使它成为头结点。
当然,前一个方法更加直观,在算法上也更加有“蛇”的效果,因此采用了前者。
还有一个贪吃蛇移动的小问题,注意到贪吃蛇是不可以向后走的,即连续两次的移动方向不可以相反,否则蛇头就会沿着自己的身体前进,直接造成“撞墙”,从而GameOver,而这是我们不愿意看到的,因此要考虑避开这种情况。
而贪吃蛇移动时,移速的改变可以依靠一个全局变量dtime,蛇的每次移动都延时dtime的时间,初始化的贪吃蛇的移动速度为1500ms,则只要将dtime的初值设置为1500,在蛇每次吃到苹果时将dtime的值减少10,就可以做到不断加速的效果。
3.实验程序设计
代码如下:
#include
#include
#include
#include
#include
#defineMX(x)((x)*10+121)
#defineMY(y)((y)*10+41)
structmysnake{
intx;
inty;
intDir;
structmysnake*next;
structmysnake*last;//prev
};
struct{
intflag;
intpoint;
}Point[42][42];
intesc=0;
intHdir=SDLK_RIGHT;
SDL_Surface*screen;
intdtime=180;
voidSetGame();
voidProgress();
voidSetApple();
structmysnake*Move(structmysnake*head,structmysnake*tail,intmovex,intmovey);
voidClean(structmysnake*head);
voidSnake(structmysnake*head);
intmain(void)
{
if(SDL_Init(SDL_INIT_VIDEO)<0){
fprintf(stderr,"Initvideofailed:
%s\n",SDL_GetError());
exit
(1);
}
screen=SDL_SetVideoMode(640,480,16,SDL_SWSURFACE);
if(screen==NULL){
fprintf(stderr,"Setvideomodefailed:
%s\n",SDL_GetError());
exit
(1);
}
atexit(SDL_Quit);
SetGame();
getchar();
return0;
}
voidSetGame()
{
inti,j;
for(i=0;i<42;i++)
for(j=0;j<42;j++){
Point[i][j].flag=0;
Point[i][j].point=0;
}
for(i=0;i<42;i++)
Point[0][i].flag=1;
for(i=0;i<42;i++)
Point[41][i].flag=1;
for(i=0;i<42;i++)
Point[i][0].flag=1;
for(i=0;i<42;i++)
Point[i][41].flag=1;
Draw_Rect(screen,129,49,402,402,SDL_MapRGB(screen->format,0,0,255));
Draw_FillRect(screen,130,50,400,400,SDL_MapRGB(screen->format,255,255,255));
SDL_WaitEvent(NULL);
Progress();
}
voidProgress()
{
structmysnake*head,*tail;
intkey;
SDL_Eventevent;
head=(structmysnake*)malloc(sizeof(structmysnake));
tail=(structmysnake*)malloc(sizeof(structmysnake));
head->next=tail;
head->last=NULL;
head->x=20;
head->y=20;
head->last=NULL
head->Dir=RIGHT;
Point[20][20].flag=1;
tail->next=NULL;
tail->last=head;
tail->x=19;
tail->y=20;
tail->Dir=RIGHT;
Point[19][20].flag=1;
Snake(head);
SetApple();
while(!
esc){
SDL_Delay(dtime);
while(SDL_PollEvent(&event)){
if(event.type==SDL_KEYDOWN)
switch(event.type){
caseSDLK_RIGHT:
if(before!
=SDLK_LEFT)Hdir=SDLK_RIGHT;break;
caseSDLK_LEFT:
if(before!
=SDLK_RIGHT)Hdir=SDLK_LEFT;break;
caseSDLK_DOWN:
if(before!
=SDLK_UP)Hdir=SDLK_DOWN;break;
caseSDLK_UP:
if(before!
=SDLK_DOWN)Hdir=SDLK_UP;break;
caseSDLK_SPACE:
SDL_WaitEvent(NULL);break;
caseSDLK_ESCAPE:
fprintf(stdin,"CompulsoryWithdrawal!
\n");
SDL_Delay(10);
esc=1;
break;
}
elseif(event.type==SDL_QUIT)esc=1;
}
switch(Hdir){
caseSDLK_RIGHT:
head=Move(head,tail,1,0);break;
caseSDLK_LEFT:
head=Move(head,tail,-1,0);break;
caseSDLK_DOWN:
head=Move(head,tail,0,1);break;
caseSDLK_UP:
head=Move(head,tail,0,-1);break;
}
SDL_UpdateRect(screen,0,0,0,0);
}
}
voidSetApple()
{
intPX=0,PY=0;
srand(time(NULL));
while(Point[PX][PY].flag){
PX=rand()%40;
PY=rand()%40;
}
delay(10);
Point[PX][PY].point=1;
Draw_FillRect(screen,MX(PX),MY(PY),8,8,SDL_MapRGB(screen->format,255,255,0));
}
structmysnake*Move(structmysnake*head,structmysnake*tail,intmovex,intmovey)
{
structmysnake*p;
if(Point[head->x+movex][head->y+movey].flag){
fprintf(stdin,"GameOver!
\n");
SDL_Delay(10);
esc=1;
returnhead;
}
Clean(head);
if(Point[head->x+movex][head->y+movey].point){
Point[head->x+movex][head->y+movey].point=0;
Point[head->x+movex][head->y+movey].flag=1;
p=(structmysnake*)malloc(sizeof(structmysnake));
head->last=p;
p->next=head;
p->x=head->x+movex;
p->y=head->y+movey;
head=p;
head->Dir=Hdir;
if(dtime>=50)dtime=dtime-5;
SetApple();
}
elseif(!
Point[head->x+movex][head->y+movey].flag){
head->x=head->x+movex;
head->y=head->y+movey;
for(p=tail;p->last!
=NULL;p=p->last)
p->Dir=p->last->Dir;
head->Dir=Hdir;
for(p=head->next;p!
=NULL;p=p->next){
switch(p->Dir){
caseSDLK_RIGHT:
p->x++;break;
caseSDLK_LEFT:
p->x--;break;
caseSDLK_DOWN:
p->y++;break;
caseSDLK_UP:
p->y--;break;
}
}
}
Snake(head);
returnhead;
}
voidClean(structmysnake*head)
{
structmysnake*p;
for(p=head;p!
=NULL;p=p->next){
Point[p->x][p->y].flag=0;
Draw_FillRect(screen,MX(p->x),MY(p->y),8,8,SDL_MapRGB(screen->format,255,255,255));
}
}
voidSnake(structmysnake*head)
{
structmysnake*p;
for(p=head;p!
=NULL;p=p->next){
Point[p->x][p->y].flag=1;
Draw_FillRect(screen,MX(p->x),MY(p->y),8,8,SDL_MapRGB(screen->format,0,255,255));
}
Draw_FillRect(screen,MX(head->x),MY(head->y),8,8,SDL_MapRGB(screen->format,255,0,0));
}
4.实验结果分析
运行画面截图:
(1)开始时贪吃蛇只有头尾两节。
(2)贪吃蛇运动时
(3)撞到墙之后死亡
(4)死亡后画面变红,色彩渐变,并在终端输出GameOver,游戏结束。
(5)运行画面视频
视频录制先于修改代码,游戏结束后画面没有发生变化,但会在终端输出GameOver。
详情见附件:
贪吃蛇运行画面视频.rmvb(打不开请见外部文件)
5.结语
在程序的设计过程中,我也遇到了许多其他问题。
首先是Linux环境下,图形库与TurboC完全不同,在TC环境下的delay函数、bar函数等都无法使用,因此要采用Linux自带的图形库。
众多图形库中,我选择的是SDL图形开发库,由于ubuntu中没有现成的相关头文件,因此需要进行安装,安装的过程也耗费了我相当多的时间。
而在之后的应用中,我也要从头学习矩形的绘制、画面的更新等操作。
当然,贪吃蛇的核心程序从无到有的建立也让我更加熟练地掌握了C语言的编程。
6.参考文献
[1]刘加海,严冰,季江民.Linux程序设计[M].杭州:
浙江大学出版社.2012:
317-376
-----精心整理,希望对您有所帮助!
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux 基于 SDL 图形 贪吃 游戏