(一)程序所包含的的模塊
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 <time.h>
#include <stdlib.h>
#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\n");
exit(0);
}
if(TTF_Init() < 0)
{
printf("Fail to init the font:%s\n",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 !\n");
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 !!!\n",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("你按了一個錯誤的按鍵,該信號已被屏蔽 !\n");
return 0;
}
else
*key = 'U';
break;
case SDLK_DOWN:
if(*key == 'U')
{
printf("你按了一個錯誤的按鍵,該信號已被屏蔽 !\n");
return 0;
}
*key = 'D';
break;
case SDLK_LEFT:
if(*key == 'R')
{
printf("你按了一個錯誤的按鍵,該信號已被屏蔽 !\n");
return 0;
}
*key = 'L';
break;
case SDLK_RIGHT:
if(*key == 'L')
{
printf("你按了一個錯誤的按鍵,該信號已被屏蔽 !\n");
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 ! \n");
(*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<len;i++)
{
if((p[i].x == rect.x)&&(p[i].y == rect.y))
return 1;
}
return 0;
}
int Is_GameOver(SDL_Rect *p,int len)//遊戲是否結束
{
int i;
for(i=0;i<len-2;i++)
{
if((p[len -1].x ==p[i].x)&&( p[len -1].y ==p[i].y ))
{
printf("撞到了自己\n");
return 2;
}
}
if((p[len -1].x== 0)||(p[len -1].x == 23*20) ||(p[len -1].y== 0)||(p[len -1].y == 23*20))
{
printf("撞到了牆壁\n");
return 1;
}
return 0;
}
void Wait_Event(int key,int len ,int *speed)
{
int flag =1;
char ch[2][20] ={{"Game Over !"},{"Win Next !"}};
SDL_Color red = {255,0,0,0};
if(key == 3)
{
rect.x = 0;
rect.y =150;
Ttf_Font(ch[0],120,&rect,red);
}
if(key == 4)
{
rect.x = 0;
rect.y =150;
Ttf_Font(ch[1],120,&rect,red);
}
SDL_Event event;
while(flag)
{
SDL_WaitEvent(&event);
switch(event.type)
{
case SDL_KEYUP:
if(event.key.keysym.sym == SDLK_SPACE)
{
flag = 0;
printf("系統暫停\n");
};
break;
case SDL_QUIT:
printf("quit\n");
flag = 0;
break;
}
SDL_UpdateRect(screen,0,0,0,0);
}
}
void Menu()
{
SDL_Surface *imagemenu = Image_Load("snack.jpg");
Show_Image(imagemenu,0);
int i =0;
SDL_BlitSurface(imagemenu, NULL , screen , 0);
rect.x = 0,rect.h = 480,rect.y =0;
for( ; i< 33;i++)
{
rect.w =20*i;
SDL_Delay(80);
SDL_UpdateRects(screen , 1 , &rect );
}
Wait_Event(1,0,0);
}
void Next_Stop(SDL_Surface * image,SDL_Surface * background,SDL_Rect move[])
{
Create_Screen(640,480,0,SDL_SWSURFACE);
move[0].x = 220;
move[0].y = 240;
move[1].x =move[0].x + 20;
move[1].y = move[0].y;
Show_Image(background,0);
Show_Image( image , &move[0]);
Load_Wall(image) ;
}
void Load_playbackground()
{
rect.x = 480;
rect.y =0;
SDL_Surface * back = Image_Load("shuihu.jpg");
Show_Image(back,&rect);
}
int main(int argc,char **argv)
{
int flag = 1, len = 2 ,is_show = 1,speed =300;
//蛇初始移動方向
char ch = 'R' ;
SDL_Rect move[ MAXLEN ] , rect_food;
Init_function();
Create_Screen(640,480,0,SDL_SWSURFACE);
Menu();
SDL_Surface * image = Image_Load("song.jpg");
SDL_Surface * image_wall= Image_Load("zhalan.jpg");
SDL_Surface * image_food= Image_Load("gao.jpg");
Create_Screen(640,480,0,SDL_SWSURFACE);
background = Image_Load("backk.jpg");
Load_playbackground();
move[0].x = 220;
move[0].y = 240;
move[1].x =move[0].x + 20;
move[1].y = move[0].y;
Show_Image(background,0);
Show_Image( image , &move[0]);
Load_Wall(image_wall) ;
srand(time(NULL));
SDL_Event event;
while(flag)
{
//出現宋江這個蛇
Image_jump(move,len,ch, image);
if(is_show) // 判斷 food 是否顯示
{
do
{
rect_food.x = (rand()%21 +2) *20;
rect_food.y = (rand()%21 +2) *20;
Show_Image(image_food, &rect_food);
}while(Reset_Food(move,len,rect_food));
is_show = 0;
}
if(Eat_Food(move,rect_food,& len) == 0)
{
is_show = 1;
}
if(Is_GameOver(move,len) > 0)
{
printf("Game over !\n");
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\n",event.button.x,event.button.y);
break;
case SDL_MOUSEBUTTONUP:
break;
case SDL_QUIT:
printf("quit\n");
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 下。
當把這些問題一一解決了,再次運行可執行文件看到貪食蛇出現在開發板上,連接一個鍵盤可以控制它上下左右移動,喫食物的時候內心非常的高興。真心的覺得只有付出纔有收穫,當我們把這些工作都完成,回過頭來思考代碼編寫,以及移植的過程,覺得這個過程並不難,最重要的是思路要清晰,事情進行到哪一步下一步該幹啥這個要清楚。在代碼編寫的過程中要儘可能的把所有的出現的情況都考慮清楚,這就是要把需求分析做好,這樣纔可以避免代碼來回的修改。需求分析涵蓋了程序中出現的各種情況的解決辦法和整體的佈局,只有把用戶需求弄明白,才能在代碼編寫的過程中避免出現對某種情況的遺漏。
總之,做事不怕難,難的是是自己捨不得去做,只有去做了,纔不怕困難,因爲遇到問題要想盡千方百計去解決,才能把事情做好,如果你不願意解決困難和問題,困哪和問題永遠就擺在那裏等着你去解決,你不去做永遠也解決不了這些困難。通過這次試驗,我們認識到了團隊的重要性,一個人也能完成任務,但是效率和質量就沒有在團隊中大家集思廣益完成的優秀,因爲一個人的思維必定存在一定的侷限性,而經過大家一起討論之後,不僅能解決遇到的問題,還能總結出更加有創意的方法。