突发奇想:
最近又得开始忙着复习考试了,整天都是看PPT,啃课本,做习题,无聊加烦恼。手痒,想敲代码,又怕上瘾耽误了考试,所以就想着拿个小程序来练练手,以解烦闷。正在做视频监控,对SDL有点了解,所以就它了!而且它是跨平台的,方便在linux下玩弄。做个什么好呢?就来个QQ表情版的贪吃蛇好了,我觉得做出来会很搞笑的。说干就干,这篇blog就是在这种冲动下写的,嘻嘻嘻~~为了写好这篇blog,我决定使用我们学校整天吹嘘的CDIO理念来组织文章,所以这对我又是一个有意思的挑战。好了,废话不多说,哥开始了……
(一)构思(CDIO中的C)
QQ表情版的贪吃蛇使用QQ经典表情来充当蛇身和食物的,其中头像当蛇身,其他的当食物。这些表情是按照一定顺序来出来的,也许可以使用循环数组的方法实现之~~至于游戏规则就没什么好讲的了,就是前后左右键盘响应控制蛇身方向,食物随机位置出现,蛇身吃完食物后蛇身便长长一节,分数便增加一分。
(二)设计(CDIO中的D)
关键部分就是蛇的移动,我把整条蛇设计成一条单向循环链表,并且是逆向的,即从蛇尾蛇一直指向蛇头,再从蛇头指回蛇尾,当蛇移动时,只要把蛇头指针向前移动一节(即蛇尾,移动之前已经将其改为新蛇头),同时蛇尾指针也向向前移动一节(即次蛇尾),而中间部分的蛇节点的结构体不需要改变,这样用户就会感觉是蛇在移动。
次关键部分就是蛇的增长,当蛇头吃到食物之后,就根据旧蛇的蛇尾记录产生新的节点,然后插到蛇尾即可
是不是很简单啊!!那接下来开始实现吧~~
(三)实现(CDIO中的I)
#include<stdio.h> #include<stdlib.h> #include <SDL/SDL.h> #include <SDL/SDL_image.h> #include <SDL/SDL_ttf.h> #define down 1 #define left 2 #define right 3 #define up 4 SDL_Surface *screen; SDL_Surface *start_background; SDL_Surface *run_backgound; SDL_Surface *snake_node; SDL_Surface *food; SDL_Surface *score; SDL_Rect dst; SDL_Color color; TTF_Font *font; char str[10]; /*蛇节点*/ typedef struct Body_Node{ int i,j;//坐标 struct Body_Node *p;//指向下一蛇节点 }Body_Node; /*蛇*/ typedef struct Snake{ Body_Node *head,*tail;//蛇头蛇尾指针 Body_Node tail_record;//旧蛇蛇尾记录(吃东西时用到) int snake_long;//蛇身长度 int direction;//蛇头方向 }Snake; /*食物*/ typedef struct Food{ int i,j;//坐标 int num;//图片编号 }Food; SDL_Surface *LoadIMG(const char *name) { SDL_Surface *tmp, *final; if ((tmp = IMG_Load (name)) == NULL) fprintf (stderr, "load %s error\n", name); final = SDL_DisplayFormat (tmp); SDL_FreeSurface (tmp); SDL_SetColorKey (final, SDL_SRCCOLORKEY | SDL_RLEACCEL,*(Uint32 *) final->pixels); return final; } /*初始化*/ void Init() { if ((SDL_Init (SDL_INIT_AUDIO | SDL_INIT_VIDEO)) < 0) fprintf (stderr, "init error\n"); if ((screen = SDL_SetVideoMode (720, 720, 32, SDL_HWSURFACE | SDL_DOUBLEBUF)) == NULL) fprintf (stderr, "set video mode error\n"); if (TTF_Init() < 0) fprintf(stderr, "TTF init error:%s\n", SDL_GetError()); atexit (SDL_Quit); SDL_WM_SetCaption ("Greed Snake", NULL); srand (time (NULL)); run_backgound = LoadIMG ("background.png"); start_background = LoadIMG ("start.jpg"); } /*绘画游戏开始界面*/ void Game_start() { int i,j; int temp=30; int x=0, y=0; int num=0; int flag=0; dst.x = 0;//绘画游戏开始界面的背景 dst.y = 0; dst.w = start_background->w; dst.h = start_background->h; SDL_BlitSurface (start_background, NULL, screen, &dst); SDL_UpdateRects (screen, 1, &dst); for(i=1; i<=31;)//绘画游戏开始界面的动画 { for(j=1; j<= temp; j++) { snprintf(str, 10, "%d.gif", num);//绘画一个QQ表情 start_background = LoadIMG (str); SDL_Delay (15); dst.x = x; dst.y = y; dst.w = start_background->w; dst.h = start_background->h; SDL_BlitSurface (start_background, NULL, screen, &dst); SDL_UpdateRects (screen, 1, &dst); num = (num+1)%82;//QQ表情图片的编号控制在0到81 /*设置一个flag,动画时,分别有(1)x轴递增,y轴不变;(2)x轴不变,y轴递减;(3)x轴递减,y轴不变;(4)x轴不变,y轴递增;四种状态*/ switch(flag) { case 0:x += 24;break; case 1:y += 24;break; case 2:x -= 24;break; case 3:y -= 24;break; } } i++; temp = (32-i)-(31-i)%2;//每画一条边时,控制需要绘画的QQ表情个数 switch(flag)//坐标超出屏幕,需要回溯 { case 0:{x -= 24;y += 24;}break; case 1:{x -= 24;y -= 24;}break; case 2:{x += 24;y -= 24;}break; case 3:{x += 24;y += 24;}break; } flag = (flag+1)%4;//flag控制在0到3 } } /*绘画游戏进行时背景,即“QQ聊天窗口”*/ void Game_background() { dst.x = 0;//设置目标边框dst(x=0,y=0,width=w,height=h) dst.y = 0; dst.w = run_backgound->w; dst.h = run_backgound->h; SDL_BlitSurface (run_backgound, NULL, screen, &dst);//copy到主窗口(screen)上 SDL_UpdateRects (screen, 1, &dst);//局部更新主窗口 } /*绘画游戏结束时的提示,即“GameOver”*/ void Game_end() { font = TTF_OpenFont("test.ttf", 100);//设置字体样式与大小 color.r = 255; color.g = 0; color.b = 0; score=TTF_RenderText_Solid(font, "Game Over", color); dst.x = 150; dst.y = 350; dst.w = score->w; dst.h = score->h; SDL_BlitSurface(score, NULL, screen, &dst); SDL_UpdateRects (screen, 1, &dst); } /*绘画游戏进行时分数*/ void Draw_Score(Snake S) { snprintf(str, 10, "%d", S.snake_long); font = TTF_OpenFont("test.ttf", 15); color.r = 0; color.g = 0; color.b = 255; score=TTF_RenderText_Solid(font, str, color); dst.x = 278; dst.y = 122; dst.w = score->w; dst.h = score->h; SDL_BlitSurface(score, NULL, screen, &dst); SDL_UpdateRects (screen, 1, &dst); } /*绘画食物*/ void Draw_Food(Food F) { snprintf(str, 10, "%d.gif", F.num); food = LoadIMG (str); dst.x = F.i*24; dst.y = F.j*24+210; dst.w = food->w; dst.h = food->h; SDL_BlitSurface (food, NULL, screen, &dst); SDL_UpdateRects (screen, 1, &dst); } /*绘画蛇*/ void Draw_Snake(Snake S) { int k; int num; Body_Node *tp; tp=S.head; for(num=k=0;k<S.snake_long;k++,num=(num+1)%65) { snprintf(str, 10, "%d.gif", num); snake_node = LoadIMG (str); dst.x = tp->i*24; dst.y = tp->j*24+210; dst.w = snake_node->w; dst.h = snake_node->h; SDL_BlitSurface (snake_node, NULL, screen, &dst); SDL_UpdateRects (screen, 1, &dst); tp=tp->p; SDL_FreeSurface(snake_node); } } /*生产食物*/ void Produce_Food(Food *F) { time_t t; int i,j; srand((unsigned)time(&t)); F->i=(rand()%29); F->j=(rand()%20); F->num=(rand()%16)+65; } /*生产蛇,最初为2节*/ void Produce_Snake(Snake *S) { Body_Node *temp; temp=(Body_Node *)malloc(sizeof(Body_Node)); temp->i=4; temp->j=3; S->tail=temp; temp=(Body_Node *)malloc(sizeof(Body_Node)); temp->i=5; temp->j=3; S->head=temp; S->tail->p=S->head; S->head->p=S->tail; S->snake_long=2; S->direction=right; S->tail_record.i=-1; S->tail_record.j=-1; S->tail_record.p=NULL; } /*移动蛇身*/ void Move_Snake(Snake *S) { S->tail_record.i=S->tail->i; S->tail_record.j=S->tail->j; /*根据蛇的方向把蛇尾节点当新的蛇头节点,我把整条蛇设计成一个单向循环链表*/ switch(S->direction) { case left:{ S->tail->i=S->head->i-1; S->tail->j=S->head->j; break; } case down:{ S->tail->i=S->head->i; S->tail->j=S->head->j+1; break; } case right:{ S->tail->i=S->head->i+1; S->tail->j=S->head->j; break; } case up:{ S->tail->i=S->head->i; S->tail->j=S->head->j-1; break; } } S->head=S->tail; S->tail=S->tail->p; } /*根据键盘响应改变蛇的方向*/ void Change_Snake_Direction(SDL_Event event,Snake *S) { switch(event.key.keysym.sym) { case SDLK_LEFT: if(S->direction!=right) S->direction=left; break; case SDLK_DOWN: if(S->direction!=up) S->direction=down; break; case SDLK_RIGHT: if(S->direction!=left) S->direction=right; break; case SDLK_UP: if(S->direction!=down) S->direction=up; break; default:; } } /*当吃到食物时,增长蛇身*/ void Add_Snake_Node(Snake *S) { Body_Node *tp; tp=(Body_Node *)malloc(sizeof(Body_Node)); tp->i=S->tail_record.i; tp->j=S->tail_record.j; tp->p=S->tail; S->head->p=tp; S->tail=tp; S->snake_long++; } /*判断蛇是否存活*/ void Judge_life(Snake S,int *life) { int k; Body_Node *tp=S.tail; for(k=S.snake_long;k>=5;k--,tp=tp->p)//判断是否撞到自己的身体,只有5节蛇身才会出现此问题 { if(S.head->i==tp->i&&S.head->j==tp->j) { *life=0; break; } } if(S.head->i<0||S.head->i>28)//判断蛇头是否撞到墙壁 { *life=0; } else if(S.head->j<0||S.head->j>19) { *life=0; } } /*测试用的,方便调试*/ void Test(Snake S,Food F) { Body_Node *tp; int k; tp=S.tail; printf("==========Snake==========\n"); for(k=0;k<S.snake_long;k++) { printf("i=%d j=%d\n",tp->i,tp->j); tp=tp->p; } printf("==========Food===========\n"); printf("i=%d j=%d\n",F.i,F.j); } int main() { int life=1; Snake S; Food F; SDL_Event event; Init(); Produce_Food(&F); Produce_Snake(&S); Game_start(); Game_background(); while(life) { Game_background(); Draw_Food(F); Draw_Snake(S); Draw_Score(S); SDL_Delay (300); while (SDL_PollEvent (&event)) { switch (event.type) { case SDL_QUIT: life = 0; break; case SDL_KEYDOWN: Change_Snake_Direction(event,&S); break; default:; } } Move_Snake(&S); if(S.head->i==F.i&&S.head->j==F.j) { Add_Snake_Node(&S);//Test(S,F); Produce_Food(&F); } Judge_life(S,&life); } Game_end(); getchar(); return 0; }具体的源代码可到http://download.youkuaiyun.com/detail/xuyuanfan77/3909064下载……
(四)操作(CDIO中的O)
至于操作嘛,我还真不知一个软件有什么好操作的,也许是运维吧,在前面我也已经展现出了运行的照片了,也就没啥好说了。不过有点体会倒是很深,那就是一定要一定要海读有质量的源代码~~