《QQ表情版贪吃蛇游戏》项目实作【附代码】


突发奇想:

最近又得开始忙着复习考试了,整天都是看PPT,啃课本,做习题,无聊加烦恼。手痒,想敲代码,又怕上瘾耽误了考试,所以就想着拿个小程序来练练手,以解烦闷。正在做视频监控,对SDL有点了解,所以就它了!而且它是跨平台的,方便在linux下玩弄。做个什么好呢?就来个QQ表情版的贪吃蛇好了,我觉得做出来会很搞笑的。说干就干,这篇blog就是在这种冲动下写的,嘻嘻嘻~~为了写好这篇blog,我决定使用我们学校整天吹嘘的CDIO理念来组织文章,所以这对我又是一个有意思的挑战。好了,废话不多说,哥开始了……

(一)构思(CDIO中的C)

QQ表情版的贪吃蛇使用QQ经典表情来充当蛇身和食物的,其中头像当蛇身,其他的当食物。这些表情是按照一定顺序来出来的,也许可以使用循环数组的方法实现之~~至于游戏规则就没什么好讲的了,就是前后左右键盘响应控制蛇身方向,食物随机位置出现,蛇身吃完食物后蛇身便长长一节,分数便增加一分。

(二)设计(CDIO中的D)

关键部分就是蛇的移动,我把整条蛇设计成一条单向循环链表,并且是逆向的,即从蛇尾蛇一直指向蛇头,再从蛇头指回蛇尾,当蛇移动时,只要把蛇头指针向前移动一节(即蛇尾,移动之前已经将其改为新蛇头),同时蛇尾指针也向向前移动一节(即次蛇尾),而中间部分的蛇节点的结构体不需要改变,这样用户就会感觉是蛇在移动。

次关键部分就是蛇的增长,当蛇头吃到食物之后,就根据旧蛇的蛇尾记录产生新的节点,然后插到蛇尾即可

是不是很简单啊!!那接下来开始实现吧~~

《贪吃蛇游戏》项目实作【附代码】
《贪吃蛇游戏》项目实作【附代码】
《贪吃蛇游戏》项目实作【附代码】
《贪吃蛇游戏》项目实作【附代码】
《贪吃蛇游戏》项目实作【附代码】

(三)实现(CDIO中的I)

  1. #include<stdio.h>   
  2.   
  3. #include<stdlib.h>   
  4.   
  5. #include <SDL/SDL.h>   
  6.   
  7. #include <SDL/SDL_image.h>   
  8.   
  9. #include <SDL/SDL_ttf.h>   
  10.   
  11.   
  12.   
  13. #define down 1   
  14.   
  15. #define left 2   
  16.   
  17. #define right 3   
  18.   
  19. #define up 4   
  20.   
  21.   
  22.   
  23. SDL_Surface *screen;  
  24.   
  25. SDL_Surface *start_background;  
  26.   
  27. SDL_Surface *run_backgound;  
  28.   
  29. SDL_Surface *snake_node;  
  30.   
  31. SDL_Surface *food;  
  32.   
  33. SDL_Surface *score;  
  34.   
  35.   
  36.   
  37. SDL_Rect dst;  
  38.   
  39. SDL_Color color;  
  40.   
  41. TTF_Font *font;  
  42.   
  43. char str[10];  
  44.   
  45.   
  46.   
  47. /*蛇节点*/   
  48.   
  49. typedef struct Body_Node{  
  50.   
  51.         int i,j;//坐标   
  52.   
  53.         struct Body_Node *p;//指向下一蛇节点   
  54.   
  55. }Body_Node;   
  56.   
  57.   
  58. /*蛇*/  
  59.   
  60. typedef struct Snake{  
  61.   
  62.         Body_Node *head,*tail;//蛇头蛇尾指针   
  63.         Body_Node tail_record;//旧蛇蛇尾记录(吃东西时用到)   
  64.   
  65.         int snake_long;//蛇身长度   
  66.   
  67.         int direction;//蛇头方向   
  68.   
  69. }Snake;  
  70.   
  71.   
  72. /*食物*/  
  73.   
  74. typedef struct Food{  
  75.   
  76.         int i,j;//坐标   
  77.   
  78.         int num;//图片编号   
  79.   
  80. }Food;  
  81.   
  82.   
  83.   
  84. SDL_Surface *LoadIMG(const char *name)  
  85.   
  86. {  
  87.   
  88.     SDL_Surface *tmp, *final;  
  89.   
  90.     if ((tmp = IMG_Load (name)) == NULL)  
  91.   
  92.         fprintf (stderr, "load %s error\n", name);  
  93.   
  94.     final = SDL_DisplayFormat (tmp);  
  95.   
  96.     SDL_FreeSurface (tmp);  
  97.   
  98.     SDL_SetColorKey (final, SDL_SRCCOLORKEY | SDL_RLEACCEL,*(Uint32 *) final->pixels);  
  99.   
  100.     return final;  
  101.   
  102. }  
  103.   
  104.   
  105. /*初始化*/  
  106.   
  107. void Init()  
  108.   
  109. {  
  110.   
  111.     if ((SDL_Init (SDL_INIT_AUDIO | SDL_INIT_VIDEO)) < 0)  
  112.   
  113.         fprintf (stderr, "init error\n");  
  114.   
  115.     if ((screen = SDL_SetVideoMode (720, 720, 32, SDL_HWSURFACE | SDL_DOUBLEBUF)) == NULL)  
  116.   
  117.         fprintf (stderr, "set video mode error\n");  
  118.   
  119.     if (TTF_Init() < 0)  
  120.   
  121.             fprintf(stderr, "TTF init error:%s\n", SDL_GetError());  
  122.   
  123.     atexit (SDL_Quit);  
  124.   
  125.     SDL_WM_SetCaption ("Greed Snake", NULL);  
  126.   
  127.     srand (time (NULL));  
  128.   
  129.     run_backgound = LoadIMG ("background.png");  
  130.   
  131.     start_background = LoadIMG ("start.jpg");  
  132.   
  133. }  
  134.   
  135.   
  136. /*绘画游戏开始界面*/  
  137.   
  138. void Game_start()  
  139.   
  140. {  
  141.   
  142.     int i,j;  
  143.   
  144.     int temp=30;  
  145.   
  146.     int x=0, y=0;  
  147.   
  148.     int num=0;  
  149.   
  150.     int flag=0;  
  151.   
  152.       
  153.   
  154.     dst.x = 0;//绘画游戏开始界面的背景   
  155.   
  156.     dst.y = 0;  
  157.   
  158.     dst.w = start_background->w;  
  159.   
  160.     dst.h = start_background->h;  
  161.   
  162.     SDL_BlitSurface (start_background, NULL, screen, &dst);  
  163.   
  164.     SDL_UpdateRects (screen, 1, &dst);  
  165.   
  166.   
  167.   
  168.     for(i=1; i<=31;)//绘画游戏开始界面的动画   
  169.   
  170.     {  
  171.   
  172.         for(j=1; j<= temp; j++)  
  173.   
  174.         {  
  175.   
  176.             snprintf(str, 10, "%d.gif", num);//绘画一个QQ表情   
  177.   
  178.             start_background = LoadIMG (str);  
  179.   
  180.             SDL_Delay (15);  
  181.   
  182.             dst.x = x;  
  183.   
  184.             dst.y = y;  
  185.   
  186.             dst.w = start_background->w;  
  187.   
  188.             dst.h = start_background->h;  
  189.   
  190.             SDL_BlitSurface (start_background, NULL, screen, &dst);  
  191.   
  192.             SDL_UpdateRects (screen, 1, &dst);  
  193.   
  194.               
  195.   
  196.             num = (num+1)%82;//QQ表情图片的编号控制在0到81   
  197.             /*设置一个flag,动画时,分别有(1)x轴递增,y轴不变;(2)x轴不变,y轴递减;(3)x轴递减,y轴不变;(4)x轴不变,y轴递增;四种状态*/  
  198.   
  199.             switch(flag)  
  200.   
  201.             {  
  202.   
  203.                 case 0:x += 24;break;  
  204.   
  205.                 case 1:y += 24;break;  
  206.   
  207.                 case 2:x -= 24;break;  
  208.   
  209.                 case 3:y -= 24;break;  
  210.   
  211.             }  
  212.   
  213.         }  
  214.   
  215.         i++;  
  216.   
  217.         temp = (32-i)-(31-i)%2;//每画一条边时,控制需要绘画的QQ表情个数   
  218.   
  219.         switch(flag)//坐标超出屏幕,需要回溯   
  220.   
  221.         {  
  222.   
  223.             case 0:{x -= 24;y += 24;}break;  
  224.   
  225.             case 1:{x -= 24;y -= 24;}break;  
  226.   
  227.             case 2:{x += 24;y -= 24;}break;  
  228.   
  229.             case 3:{x += 24;y += 24;}break;  
  230.   
  231.         }  
  232.   
  233.         flag = (flag+1)%4;//flag控制在0到3   
  234.   
  235.     }  
  236.   
  237. }  
  238.   
  239.   
  240. /*绘画游戏进行时背景,即“QQ聊天窗口”*/  
  241.   
  242. void Game_background()  
  243.   
  244. {  
  245.   
  246.     dst.x = 0;//设置目标边框dst(x=0,y=0,width=w,height=h)   
  247.   
  248.     dst.y = 0;  
  249.   
  250.     dst.w = run_backgound->w;  
  251.   
  252.     dst.h = run_backgound->h;  
  253.   
  254.     SDL_BlitSurface (run_backgound, NULL, screen, &dst);//copy到主窗口(screen)上   
  255.   
  256.     SDL_UpdateRects (screen, 1, &dst);//局部更新主窗口   
  257.   
  258. }  
  259.   
  260.   
  261. /*绘画游戏结束时的提示,即“GameOver”*/  
  262.   
  263. void Game_end()  
  264.   
  265. {  
  266.   
  267.     font = TTF_OpenFont("test.ttf", 100);//设置字体样式与大小   
  268.   
  269.         color.r = 255;  
  270.   
  271.         color.g = 0;  
  272.   
  273.         color.b = 0;  
  274.   
  275.         score=TTF_RenderText_Solid(font, "Game Over", color);  
  276.   
  277.       
  278.   
  279.         dst.x = 150;  
  280.   
  281.         dst.y = 350;  
  282.   
  283.         dst.w = score->w;  
  284.   
  285.         dst.h = score->h;  
  286.   
  287.         SDL_BlitSurface(score, NULL, screen, &dst);  
  288.   
  289.         SDL_UpdateRects (screen, 1, &dst);  
  290.   
  291. }  
  292.   
  293.   
  294. /*绘画游戏进行时分数*/  
  295.   
  296. void Draw_Score(Snake S)  
  297.   
  298. {  
  299.   
  300.     snprintf(str, 10, "%d", S.snake_long);  
  301.   
  302.     font = TTF_OpenFont("test.ttf", 15);  
  303.   
  304.         color.r = 0;  
  305.   
  306.         color.g = 0;  
  307.   
  308.         color.b = 255;  
  309.   
  310.         score=TTF_RenderText_Solid(font, str, color);  
  311.   
  312.       
  313.   
  314.         dst.x = 278;  
  315.   
  316.         dst.y = 122;  
  317.   
  318.         dst.w = score->w;  
  319.   
  320.         dst.h = score->h;  
  321.   
  322.         SDL_BlitSurface(score, NULL, screen, &dst);  
  323.   
  324.         SDL_UpdateRects (screen, 1, &dst);  
  325.   
  326. }  
  327.   
  328.   
  329. /*绘画食物*/  
  330.   
  331. void Draw_Food(Food F)  
  332.   
  333. {  
  334.   
  335.     snprintf(str, 10, "%d.gif", F.num);  
  336.   
  337.     food = LoadIMG (str);  
  338.   
  339.     dst.x = F.i*24;  
  340.   
  341.     dst.y = F.j*24+210;  
  342.   
  343.     dst.w = food->w;  
  344.   
  345.     dst.h = food->h;  
  346.   
  347.     SDL_BlitSurface (food, NULL, screen, &dst);  
  348.   
  349.     SDL_UpdateRects (screen, 1, &dst);  
  350.   
  351. }  
  352.   
  353.   
  354. /*绘画蛇*/  
  355.   
  356. void Draw_Snake(Snake S)  
  357.   
  358. {  
  359.   
  360.         int k;  
  361.         int num;  
  362.   
  363.         Body_Node *tp;  
  364.   
  365.       
  366.   
  367.         tp=S.head;  
  368.   
  369.         for(num=k=0;k<S.snake_long;k++,num=(num+1)%65)  
  370.   
  371.         {  
  372.   
  373.             snprintf(str, 10, "%d.gif", num);  
  374.   
  375.         snake_node = LoadIMG (str);  
  376.   
  377.             dst.x = tp->i*24;  
  378.   
  379.         dst.y = tp->j*24+210;  
  380.   
  381.         dst.w = snake_node->w;  
  382.   
  383.         dst.h = snake_node->h;  
  384.   
  385.         SDL_BlitSurface (snake_node, NULL, screen, &dst);  
  386.   
  387.         SDL_UpdateRects (screen, 1, &dst);  
  388.   
  389.             tp=tp->p;  
  390.   
  391.             SDL_FreeSurface(snake_node);  
  392.   
  393.         }  
  394.   
  395. }  
  396.   
  397.   
  398. /*生产食物*/  
  399.   
  400. void Produce_Food(Food *F)  
  401.   
  402. {  
  403.   
  404.     time_t t;  
  405.   
  406.     int i,j;  
  407.   
  408.     srand((unsigned)time(&t));  
  409.   
  410.     F->i=(rand()%29);  
  411.   
  412.     F->j=(rand()%20);  
  413.   
  414.     F->num=(rand()%16)+65;  
  415.   
  416. }  
  417.   
  418.   
  419. /*生产蛇,最初为2节*/  
  420.   
  421. void Produce_Snake(Snake *S)  
  422.   
  423. {  
  424.   
  425.      Body_Node *temp;  
  426.   
  427.      temp=(Body_Node *)malloc(sizeof(Body_Node));  
  428.   
  429.      temp->i=4;  
  430.   
  431.      temp->j=3;  
  432.   
  433.      S->tail=temp;  
  434.   
  435.      temp=(Body_Node *)malloc(sizeof(Body_Node));  
  436.   
  437.      temp->i=5;  
  438.   
  439.      temp->j=3;  
  440.   
  441.      S->head=temp;  
  442.   
  443.      S->tail->p=S->head;  
  444.   
  445.      S->head->p=S->tail;  
  446.   
  447.      S->snake_long=2;  
  448.   
  449.      S->direction=right;  
  450.      S->tail_record.i=-1;  
  451.      S->tail_record.j=-1;  
  452.      S->tail_record.p=NULL;  
  453.   
  454. }  
  455.   
  456.   
  457. /*移动蛇身*/  
  458.   
  459. void Move_Snake(Snake *S)  
  460.   
  461. {   
  462.   
  463.     S->tail_record.i=S->tail->i;  
  464.     S->tail_record.j=S->tail->j;  
  465.     /*根据蛇的方向把蛇尾节点当新的蛇头节点,我把整条蛇设计成一个单向循环链表*/  
  466.     switch(S->direction)  
  467.   
  468.     {  
  469.   
  470.          case left:{  
  471.   
  472.               S->tail->i=S->head->i-1;  
  473.   
  474.               S->tail->j=S->head->j;  
  475.   
  476.               break;  
  477.   
  478.               }  
  479.   
  480.          case down:{  
  481.   
  482.               S->tail->i=S->head->i;  
  483.   
  484.               S->tail->j=S->head->j+1;  
  485.   
  486.               break;  
  487.   
  488.               }  
  489.   
  490.          case right:{  
  491.   
  492.               S->tail->i=S->head->i+1;  
  493.   
  494.               S->tail->j=S->head->j;  
  495.   
  496.               break;  
  497.   
  498.               }  
  499.   
  500.          case up:{  
  501.   
  502.               S->tail->i=S->head->i;  
  503.   
  504.               S->tail->j=S->head->j-1;  
  505.   
  506.               break;  
  507.   
  508.               }  
  509.   
  510.     }    
  511.   
  512.     S->head=S->tail;  
  513.   
  514.     S->tail=S->tail->p;         
  515.   
  516. }  
  517.   
  518.   
  519. /*根据键盘响应改变蛇的方向*/  
  520.   
  521. void Change_Snake_Direction(SDL_Event event,Snake *S)  
  522.   
  523. {  
  524.   
  525.     switch(event.key.keysym.sym)  
  526.   
  527.         {  
  528.   
  529.             case SDLK_LEFT:  
  530.   
  531.                 if(S->direction!=right)  
  532.   
  533.                     S->direction=left;  
  534.   
  535.                 break;  
  536.   
  537.         case SDLK_DOWN:  
  538.   
  539.                 if(S->direction!=up)  
  540.   
  541.                     S->direction=down;  
  542.   
  543.                 break;  
  544.   
  545.                 case SDLK_RIGHT:  
  546.   
  547.                     if(S->direction!=left)  
  548.   
  549.                         S->direction=right;  
  550.   
  551.                     break;  
  552.   
  553.                 case SDLK_UP:  
  554.   
  555.                     if(S->direction!=down)  
  556.   
  557.                         S->direction=up;  
  558.   
  559.                     break;  
  560.   
  561.                 default:;  
  562.   
  563.         }  
  564.   
  565. }  
  566.   
  567.   
  568. /*当吃到食物时,增长蛇身*/  
  569.   
  570. void Add_Snake_Node(Snake *S)  
  571.   
  572. {  
  573.   
  574.         Body_Node *tp;  
  575.   
  576.         tp=(Body_Node *)malloc(sizeof(Body_Node));  
  577.         tp->i=S->tail_record.i;  
  578.         tp->j=S->tail_record.j;  
  579.   
  580.         tp->p=S->tail;  
  581.   
  582.         S->head->p=tp;  
  583.         S->tail=tp;  
  584.   
  585.         S->snake_long++;  
  586.   
  587. }  
  588.   
  589.   
  590. /*判断蛇是否存活*/  
  591.   
  592. void Judge_life(Snake S,int *life)  
  593.   
  594. {  
  595.   
  596.         int k;  
  597.   
  598.         Body_Node *tp=S.tail;   
  599.   
  600.         for(k=S.snake_long;k>=5;k--,tp=tp->p)//判断是否撞到自己的身体,只有5节蛇身才会出现此问题   
  601.   
  602.         {  
  603.   
  604.             if(S.head->i==tp->i&&S.head->j==tp->j)  
  605.   
  606.             {  
  607.   
  608.                 *life=0;  
  609.   
  610.                 break;  
  611.   
  612.             }   
  613.   
  614.         }  
  615.   
  616.         if(S.head->i<0||S.head->i>28)//判断蛇头是否撞到墙壁   
  617.   
  618.         {  
  619.   
  620.             *life=0;  
  621.   
  622.         }  
  623.   
  624.         else if(S.head->j<0||S.head->j>19)  
  625.   
  626.         {  
  627.   
  628.             *life=0;  
  629.   
  630.         }  
  631.   
  632. }  
  633.   
  634.   
  635. /*测试用的,方便调试*/  
  636. void Test(Snake S,Food F)  
  637. {  
  638.     Body_Node *tp;  
  639.     int k;  
  640.       
  641.     tp=S.tail;  
  642.     printf("==========Snake==========\n");  
  643.     for(k=0;k<S.snake_long;k++)  
  644.     {     
  645.         printf("i=%d j=%d\n",tp->i,tp->j);  
  646.         tp=tp->p;  
  647.     }  
  648.     printf("==========Food===========\n");  
  649.     printf("i=%d j=%d\n",F.i,F.j);  
  650. }  
  651.   
  652.   
  653. int main()  
  654.   
  655. {  
  656.   
  657.         int life=1;  
  658.   
  659.         Snake S;  
  660.   
  661.         Food F;  
  662.   
  663.         SDL_Event event;  
  664.   
  665.       
  666.   
  667.         Init();  
  668.   
  669.         Produce_Food(&F);  
  670.   
  671.         Produce_Snake(&S);  
  672.   
  673.         Game_start();  
  674.   
  675.         Game_background();  
  676.   
  677.         while(life)  
  678.   
  679.         {  
  680.   
  681.             Game_background();  
  682.   
  683.             Draw_Food(F);  
  684.   
  685.             Draw_Snake(S);  
  686.   
  687.             Draw_Score(S);  
  688.   
  689.             SDL_Delay (300);  
  690.   
  691.             while (SDL_PollEvent (&event))  
  692.   
  693.         {  
  694.   
  695.             switch (event.type)  
  696.   
  697.             {  
  698.   
  699.                 case SDL_QUIT:  
  700.   
  701.                     life = 0;  
  702.   
  703.                     break;  
  704.   
  705.                 case SDL_KEYDOWN:  
  706.   
  707.                         Change_Snake_Direction(event,&S);  
  708.   
  709.                             break;  
  710.   
  711.                         default:;  
  712.   
  713.                 }  
  714.   
  715.             }  
  716.   
  717.             Move_Snake(&S);  
  718.   
  719.         if(S.head->i==F.i&&S.head->j==F.j)  
  720.   
  721.         {  
  722.   
  723.                 Add_Snake_Node(&S);//Test(S,F);   
  724.   
  725.                 Produce_Food(&F);   
  726.   
  727.             }  
  728.   
  729.             Judge_life(S,&life);  
  730.   
  731.         }  
  732.   
  733.         Game_end();  
  734.   
  735.         getchar();  
  736.   
  737.         return 0;  
  738.   
  739. }  

具体的源代码下载

下载在帮客之家的1号FTP服务器里,下载地址:

FTP地址:ftp://www.bkjia.com

用户名:www.bkjia.com

密码:www.muu.cc

在 2012年LinuxIDC.com\7月\《贪吃蛇游戏》项目实作【附代码】

下载方法见 http://www.bkjia.net/thread-1187-1-1.html

(四)操作(CDIO中的O)

至于操作嘛,我还真不知一个软件有什么好操作的,也许是运维吧,在前面我也已经展现出了运行的照片了,也就没啥好说了。不过有点体会倒是很深,那就是一定要一定要海读有质量的源代码~~

相关内容