(一)程序所包含的的模块
1)用图示的方式展示贪食蛇运行的过程。
(二)系统设计过程
贪食蛇详细实现过程:
(1)该游戏,首先进入欢迎界面,该技术使用的sdl的动画效果,用事件完成,用SDL_BlitSurface绘制图片,控制图片从左向右移动至整个屏幕。对屏幕的初始化、初始化字体库,如果出错,有初始化失败的 提示。如果初始化失败,程序就不能继续运行了,因为程序的执行是以上述内容为基础的。在游戏进入下一关和游戏结束,会出现一些简体的中文显示。
(2)加载图片后,那就要结束时释放图片所占的资源。
加载图片时要使用到SDL_Surface 指针,包括构成墙,蛇身和食物的图片,注意加载的图片的要尽量小,因为开发板所能调配的资源有限,如果图片过大会占用过多的资源。
(3)加载墙使用的两个横轴和两个纵轴组成的方格,用一个结构体数组来实现。墙的出现是通过控制坐标实现的,x轴的范围是0到32,y轴的范围是0到24,加载了zhalan.jpg这个图片。
(4)贪食蛇身体和刚开始移动的方向,是我通过Image_jump()这个函数实现的,这个函数设置了蛇身的初始大小和蛇身的初始移动方向和蛇的最大长度,初始移动方向为向右,在构成蛇身的过程中加载了song.jpg这个图片。
(5)当蛇身体出现后,用产生随机数的方法产生随机数确定食物出现的位置,设置时间种子srand(time(NULL)),用(rand()%21 +2)这个函数实现,此随机数的产生是由范围的2到22,因为食物只能出现在墙壁之内,并且不能和蛇的身体重叠。产生的食物的位置是否蛇的位置相同的需要用Reset_Food()函数做判断,如果相同,再次调用产生随机数的函数,重新产生食物,避免食物出现在蛇的身体上。
(6)游戏控制
为了是游戏在进行的过程中不出现其他的差错,我设置的只能有上下左右键和空格键和q键在键盘上能使用。
通过上下左右键控制蛇的移动。用Image_jump()这个函数实现,当程序捕捉到用户输入时,蛇的身体自动在x轴和y轴上增加和减少相应的值。
空格键控制游戏的暂停和开始,设置了flag这个参数做标记,有两个值0和1,如果此时游戏正在进行,按了空格键flag标志为0,其他的也是同样的道理。
Q键控制游戏的开始和结束最简单,用户在按了Q键时,程序只需要通过exit(0)这个函数退出即可。
(7)用户按错了按钮,我写一个函数还屏蔽这些错误,用we_keyDown()这个函数来屏蔽和纠正这些错误。举个简单的例子,当蛇正在向上移动的时候,此时如果用户按了向下移动的按钮,这个函数就把用户的按键操作转化为向上移动。其他的三种情况也是同样的道理。
(8)产生的食物是“高俅”,这也是通过加载图片产生的,当吃到食物的时候,蛇的身体上会添加一个“宋江”的图片,注意使用的图片的尺寸,不然在运行的过程中会出现食物和蛇不一致的情况,蛇头的添加是有缓冲的时间的,通过后面一个宋江走下一个宋江的位置,实际不是真正的走,而是图片转换。判断是否吃到了食物,就要判断蛇头位置是否和食物碰撞。只有吃到食物,食物才会再次随机产生。
(9)加载字体库。是用TTF_Init()和Ttf_Font()这两个函数实现的。,在游戏进入下一关的时候会用到字体库,所以就加上了字体库。
(10)游戏是否结束。如果用户在游戏中吃到的食物等于50个,本关的游戏就结束了,会出现“win next”的提示。如果用户撞到了自身或者墙游戏就会结束,产生“gameover”的提示。这些是通过函数Is_GameOver()实现的。
(11)无论程序是正常结束还是异常结束都需要释放最后释放掉加载的资源。用 SDL_FreeSurface()这个函数完成对资源的释放。
2)函数说明:
初始化函数int Init_function();
加载字体库
void Ttf_Font(charch[],int size,SDL_Rect *rect,SDL_Color color);
创建屏幕
voidCreate_Screen(int width , int height , int bpp , Uint32 flags)
加载图片
SDL_Surface *Image_Load(char *path)
显示图片
intShow_Image(SDL_Surface * image,SDL_Rect *rect)
使用图片后要释放图片
voidFree_Image(SDL_Surface * image)
按键控制
int we_keyDown(SDLKeysym , char * key )
接受背景图片
intRe_BackImage(SDL_Rect rect)
按上下左右键来控制蛇的移动
intImage_jump(SDL_Rect *p,int len,char ch ,SDL_Surface * image)
判断是否吃到食物
int Eat_Food(SDL_Rect * p,SDL_Rect rect,int *len)
设置墙壁
intLoad_Wall(SDL_Surface *wall)
判断 food的位置在不在蛇的身子上
intReset_Food(SDL_Rect *p ,int len ,SDL_Rect rect)
游戏是否结束
int Is_GameOver(SDL_Rect*p,int len)
判断游戏成功或失败,并控制游戏的暂定和开始
voidWait_Event(int key,int len ,int *speed)
加载游戏右侧的背景
voidLoad_playbackground()
(三)移植过程
1).首先在PC机上运行程序(测试)
1.将写好后的代码和程序运行所用到图片放到一个文件夹的复制到Linux系里。找到程序用到的SDL、SDL_image、SDL_ttf三个库压缩包复制到一个文件夹里。
2.将压缩包解压缩:
tar -zxvf SDL-1.2.14 .tar.gz
tar -zxvf SDL_ttf-2.0.10.tar.gz
tar -zxvf SDL_image-1.2.10.tar.gz
进入解压后的SDL-1.2.14目录下,./configure 检测平台信息,make编译,makeinstall安装。依次按上述步骤安装SDL-ttf和SDL-image。
安装完成后,去查看库文件是否生成,默认安装路径在/usr/local/lib 和/usr/local/include/SDL中。查看生成之后,
需要将所有相关的库文件复制到Linux系统的默认库文件目录下。
进入到程序文件目录下,开始编译: gcc zuyi.c -o file -lSDL -lSDL_image -lSDL_ttf
成功编译,生成可执行文件file,运行./file即可。
出现游戏画面,完成了程序在PC机上的测试。
2)在ARM-6410下挂载和运行
重新编译所需的库。(需要添加一个freetype的库,因为库文件之间的依赖关系)
依次编译,这次需要在开发板上运行,所以需要交叉编译,需要修改编译器的信息。在文件目录下,./configure后,添加指令CC=arm-linux-gcc LDSHARED=arm-linux-gcc CPP=arm-linux-gcc-E AR=arm-linux-ar rc RANLIB=arm-linux-ranlib --host=arm-linux --prefix=/usr/tt CPPFLAGS=-I/usr/tt/include
LDFLAGS=-L/usr/tt/lib
make编译,make install安装。
依照上述步骤完成三个压缩包的安装。
安装完成后转到设定的目录/usr/tt下查看两个文件夹下是否生成库文件。如果生成了,查看库的信息,检测交叉编译是否成功。
开始进行开发板的挂载。连接开发板,然后重新打开一个终端,minicom连接。在宿主机设置网络IP,ifconfig 查看网络。Ifconfigeth0(eth1) 192.168.1.6 netmask 255.255.255.0 up
查看网络是否连接成功 ping 192.168.1.199 如果连接成功。则设置共享目录 gedit /etc/exports 修改/usr/peng 192.168.1.6(rw,sync) 保存后退出。然后开启nfs服务: servicenfs restart。
把所用的图片以及程序源文件复制到这个共享文件夹下。
在连接宿主机的终端上 mountnfs 192.168.1.6:/usr/peng /mnt/nfs
在/mnt/nfs下查看是否挂在成功。再将编译好的库文件复制到共享文件夹中,再从共享文件夹下复制到开发板的默认库文件路径下: cp lib* /mnt/yaffs/Qtopia/lib
开始交叉编译 arm-linux-gcc zuyi.c -o file-L/usr/arm/lib -I/usr/arm/include/SDL -lSDL-lSDL_image -lSDL_ttf -lfreetype -lz
( -lfreetype这个是考虑到库文件依赖关系才加上的)
编译完成,生成可执行文件file 运行这个文件./file 成功在开发板上运行程序。
(四)系统实现过程
接下来用代码展示贪食蛇实现的过程,包括每个函数的作用以及调用和实现的过程,每个函数的作用已经写在函数的的开头,
所用的图片已经命名完毕,放置在系统当前目录所在的文件夹中。我的图片和代码放在一个文件夹中,系统在运行的过程中会自动在当前运行的路径中寻找到图片。
在移植的过程中要密切注意这个问题,因为如果需要的图片不放在系统当前的文件夹中,该系统会停止运行,并且报告错误找不到需要加载的图片的路径。
#include "SDL.h"
#include "SDL_image.h"
#include "math.h"
#include "string.h"
#include
#include
#include "SDL_ttf.h"
#define MAXLEN 50
SDL_Surface * screen = NULL;
SDL_Rect rect;
SDL_Surface *background ;
//初始化函数
int Init_function()
{
if(SDL_Init(SDL_INIT_VIDEO==-1))
{
printf("Init error
");
exit(0);
}
if(TTF_Init() < 0)
{
printf("Fail to init the font:%s
",SDL_GetError());
return 0;
}
}
//加载字体库
void Ttf_Font(char ch[],int size,SDL_Rect *rect,SDL_Color color)
{
TTF_Font * font = TTF_OpenFont("simfang.ttf",size);
SDL_Surface *text = TTF_RenderUTF8_Solid(font,ch,color);
SDL_BlitSurface(text,NULL,screen,rect);
SDL_UpdateRects(screen,1,&screen->clip_rect);
TTF_CloseFont(font);
}
//创建屏幕
void Create_Screen(int width , int height , int bpp , Uint32 flags)
{
screen = SDL_SetVideoMode(width , height, bpp , flags);
if(screen == NULL)
{
printf("SDL_SetVideoMode default !
");
exit(1);
}
}
//加载图片
SDL_Surface * Image_Load(char *path)
{
SDL_Surface *image = NULL;
image = IMG_Load(path);
if(image == NULL)
{
printf("The image load default, maybe can't find %s !!!
",path);
exit(0);
}
return image;
}
//显示图片
int Show_Image(SDL_Surface * image,SDL_Rect *rect)
{
SDL_BlitSurface(image , NULL , screen , rect);
SDL_UpdateRect(screen , 0 , 0 , 0 , 0 );
return 0;
}
//使用图片后要释放图片
void Free_Image(SDL_Surface * image)
{
SDL_FreeSurface(image);
}
//按键控制
int we_keyDown(SDLKey sym , char * key )
{
switch(sym)
{
case SDLK_UP:
if(*key == 'D')
{
printf("你按了一个错误的按键,该信号已被屏蔽 !
");
return 0;
}
else
*key = 'U';
break;
case SDLK_DOWN:
if(*key == 'U')
{
printf("你按了一个错误的按键,该信号已被屏蔽 !
");
return 0;
}
*key = 'D';
break;
case SDLK_LEFT:
if(*key == 'R')
{
printf("你按了一个错误的按键,该信号已被屏蔽 !
");
return 0;
}
*key = 'L';
break;
case SDLK_RIGHT:
if(*key == 'L')
{
printf("你按了一个错误的按键,该信号已被屏蔽 !
");
return 0;
}
*key = 'R';
break;
default : break;
}
return 0;
}
int Re_BackImage(SDL_Rect rect)
{
rect .w = 20;
rect .h =20;
SDL_BlitSurface(background , 0 , screen , &rect);
return 0;
}
int Image_jump(SDL_Rect *p,int len,char ch ,SDL_Surface * image)
{
SDL_Rect t;
int i;
switch(ch)
{
case 'U':
t.y = p[len -1].y -20;
t.x = p[len -1].x; break;
case 'D':
t.y = p[len -1].y +20;
t.x = p[len -1].x ; break;
case 'L':
t.y = p[len -1].y ;
t.x = p[len -1].x-20; break;
case 'R':
t.y = p[len -1].y ;
t.x = p[len -1].x+20; break;
}
Show_Image(image,&t); // 这个就是显示新的蛇头
Re_BackImage(p[0]);
for(i = 0 ;i < len ; i++ )
{
p[i].x = p[i+1].x ; // 注意数组已经越界
p[i].y = p[i+1].y ;
}
p[len -1].x = t.x;
p[len -1].y = t.y;
return 0;
}
int Eat_Food(SDL_Rect * p,SDL_Rect rect,int *len)
{
if((p[(*len)-1].x == rect.x) && (p[(*len)-1].y == rect.y))
{
printf("You have eat the food !
");
(*len) ++;
p[(*len) -1] = rect ;
return 0;
}
return -1;
}
int Load_Wall(SDL_Surface *wall) //加载墙壁
{
int x=0,y=0;
for(x = 0;x< 32;x++)
{
for(y =0 ; y<24 ;y++)
if((x== 0)||(x ==23))
{
rect.x = x*20;
rect.y = y*20;
Show_Image(wall,&rect) ;
}
}
for(y = 0;y< 24;y++)
{
for(x =0 ; x<23 ;x++)
if((y== 0)||(y ==23))
{
rect.x = x*20;
rect.y = y*20;
Show_Image(wall,&rect) ;
}
}
return 0;
}
int Reset_Food(SDL_Rect *p ,int len ,SDL_Rect rect) //判断 food的位置在不在蛇的身子上
{
int i=0;
for(;i 0)
{
printf("Game over !
");
return 0 ;
}
if(len == 40)
{
Wait_Event(4,len,&speed);
len = 2;
}
SDL_Delay(speed);
while(SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_KEYDOWN:
if(event.key.keysym.sym == SDLK_ESCAPE)
{
flag = 0;
}
we_keyDown(event.key.keysym.sym ,& ch);
break;
case SDL_KEYUP:
if (event.key.keysym.sym == SDLK_SPACE)
{
Wait_Event(2,0,0);
}
break;
case SDL_MOUSEMOTION:
break;
case SDL_MOUSEBUTTONDOWN:
printf("mouse: x = %d , y = %d
",event.button.x,event.button.y);
break;
case SDL_MOUSEBUTTONUP:
break;
case SDL_QUIT:
printf("quit
");
flag = 0;
exit(0);
break;
}
}
}
Free_Image(background);
Free_Image(image);
SDL_Quit();
return 0;
}
(五)总结和体会
在做贪食蛇的过程中,遇到了很多的问题,遇到了不会的问题就去找老师咨询、和同学讨论。做贪食蛇主要解决的以下几个问题(1)按键的控制(2)加载图片(3)创建屏幕(4)判断蛇是否吃到食物(5)安装所需要的库(6)库和可执行文件的移植。
创建屏幕和库和可执行文件移植时出现的问题最让我揪心。在创建屏幕,加载图片的时候,老出现屏幕过大或者蛇和食物不对应的问题,后来就设置了相同大小的蛇和食物的照片,用一个游戏背景来弥补屏幕过大导致的黑屏问题。的最让我纠结的是在pc端可正常运行的贪食蛇,在移植的过程中出现了很多的问题,比如在用arm-linux-gcc编译的时候出现错误,就在网上查找解决方案,原来是缺少依赖库文件,就把这个加上了。当编译成功的时候又出现图片无法加载的问题,询问同学才知道,没有把所需要的库放在恰当的位置,就把所需要的库拷贝在/mnt/yaffs/Qtopia/lib
下。
当把这些问题一一解决了,再次运行可执行文件看到贪食蛇出现在开发板上,连接一个键盘可以控制它上下左右移动,吃食物的时候内心非常的高兴。真心的觉得只有付出才有收获,当我们把这些工作都完成,回过头来思考代码编写,以及移植的过程,觉得这个过程并不难,最重要的是思路要清晰,事情进行到哪一步下一步该干啥这个要清楚。在代码编写的过程中要尽可能的把所有的出现的情况都考虑清楚,这就是要把需求分析做好,这样才可以避免代码来回的修改。需求分析涵盖了程序中出现的各种情况的解决办法和整体的布局,只有把用户需求弄明白,才能在代码编写的过程中避免出现对某种情况的遗漏。
总之,做事不怕难,难的是是自己舍不得去做,只有去做了,才不怕困难,因为遇到问题要想尽千方百计去解决,才能把事情做好,如果你不愿意解决困难和问题,困哪和问题永远就摆在那里等着你去解决,你不去做永远也解决不了这些困难。通过这次试验,我们认识到了团队的重要性,一个人也能完成任务,但是效率和质量就没有在团队中大家集思广益完成的优秀,因为一个人的思维必定存在一定的局限性,而经过大家一起讨论之后,不仅能解决遇到的问题,还能总结出更加有创意的方法。