Chinaunix首页 | 论坛 | 博客
  • 博客访问: 300791
  • 博文数量: 134
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 118
  • 用 户 组: 普通用户
  • 注册时间: 2013-08-01 14:02
文章分类

全部博文(134)

文章存档

2015年(2)

2014年(4)

2013年(128)

分类: C/C++

2013-08-01 14:17:11

原文地址:贪吃蛇 _0334 作者:丫叩酱

涉及内容:Unbuffered I/O函数
               open
               close
               read / write
               ioctl
               mmap

程序实现过程:
1. 在屏幕上打印出一点
2. 在屏幕上打印出基本图形
3. 实现用按键w、s、a、d(上下左右)控制蛇的移动方向
4. 在可吃的区域内,按规律随机产生食物。(时间到或是被吃了,立即产生食物)
5. 吃食物
6. 吃完食物,增加蛇身长度
7. game over情况:撞倒墙壁或是撞倒自己

准备工作:
1. 为提高效率,需将要操作的文件的内容映射到内存,使用mmap:
    mmap(地址, 长度, 可操作性, 共享性 , 文件描述符, 偏移量)

未知量:长度、文件
要操作的屏幕文件:    /dev/fb0      (通常都需要修改权限,才能操作)
屏幕文件的长度(字符点阵): 要知道屏幕的大小和分辨率
屏幕的宽:水平方向多少个像素
屏幕的高:竖直方向多少个像素
分辨率:一个像素有多少位
屏幕的宽、高和分辨率:在/usr/include/linux/fb.h 中获得

注意:内存中数据的存储形式是一位数组形式

2. 打印点,相当于向屏幕文件进行写操作

注意:必须在文本模式下进行运行操作


点击(此处)折叠或打开

  1. #Makefile

  2. src = $(wildcard *.c)

  3. CC = gcc
  4. CFLAGS = -Wall -g

  5. main:$(src)
  6.     $(CC) $(CFLAGS) ./frame.h $^ -o $@
  7. clean:
  8.     rm main *.o
  9. .PHONY:clean


点击(此处)折叠或打开

  1. //frame.h

  2. #ifndef _FRAME_H_
  3. #define _FRAME_H_

  4. #define RED 0x00ff0000
  5. #define GREEN 0x0000ff00
  6. #define BLUE 0x000000ff
  7. #define WHITE 0x00ffffff
  8. #define BLACK 0x00000000
  9. typedef unsigned char u8_t;
  10. typedef unsigned int u32_t;

  11. typedef struct
  12. {
  13.     int wide;
  14.     int high;
  15.     int bpp;

  16.     void *mem;
  17. }fbsrc_t;


  18. int init_data(void);   //初始化:完成映射
  19. int fb_one_pixel(int x, int y, u32_t color);      //画点
  20. int square(int x, int y,int len, u32_t color);    //画正方形方块
  21. int rectangle(int x1, int y1, int x2, int y2, u32_t color);  //画矩形框
  22. int circle(int x, int y, int radius, u32_t color);    //画圆形:已填充的
  23. int snake_move(void);  //完成蛇的移动


  24. #endif //_FRAME_H_


点击(此处)折叠或打开

  1. //init_data.c

  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. #include <sys/ioctl.h>
  7. #include <linux/fb.h>
  8. #include <sys/mman.h>
  9. #include <string.h>
  10. #include "frame.h"

  11. fbsrc_t fb_v;

  12. int init_data(void)
  13. {
  14.     int fd, len;
  15.     struct fb_var_screeninfo fb_var;

  16.     //读写屏幕文件:/dev/fb0
  17.     //运行程序时,可能会出现:permission denied
  18.     //此时序更改/dev/fb0文件的权限 sudo chmod 666 /dev/fb0
  19.     fd = open("/dev/fb0", O_RDWR);
  20.     if(fd < 0)
  21.     {
  22.         perror("/dev/fb0");
  23.         exit(1);
  24.     }

  25.     //ioctl函数:用于向设备发控制和配置命令
  26.     //ioctl函数的相关定义:在/usr/include/fb.h文件中
  27.     //查询可知,ioctl函数第二和第三个参数的相关内容
  28.     if(ioctl(fd, FBIOGET_VSCREENINFO, &fb_var) < 0)
  29.     {
  30.         perror("ioctl");
  31.         exit(1);
  32.     }

  33.     fb_v.wide = fb_var.xres; //屏幕的宽:水平方向有多少个像素
  34.     fb_v.high = fb_var.yres; //屏幕的高: 竖直方向有多少个像素
  35.     fb_v.bpp = fb_var.bits_per_pixel; //屏幕的分辨率:一个像素有多少个位组成
  36.     len = (fb_v.wide * fb_v.high * fb_v.bpp) / 8; //屏幕的大小共占的字节数


  37.     //使用mmap函数把磁盘文件的一部分直接映射到内存
  38.     fb_v.mem = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  39.     if(fb_v.mem == MAP_FAILED)
  40.     {
  41.         perror("map");
  42.         exit(1);
  43.     }
  44.     memset(fb_v.mem, 0, len);
  45.     //清屏

  46.     close(fd);
  47.     //关闭文件
  48.     

  49.     return 0;
  50. }


点击(此处)折叠或打开

  1. //basic_shapes.c

  2. #include <stdio.h>
  3. #include "frame.h"

  4. fbsrc_t fb_v;

  5. //画点:在(x, y)点上画点
  6. int fb_one_pixel(int x, int y, u32_t color)
  7. {
  8.     *((u32_t *)fb_v.mem + x + y * fb_v.wide) = color;

  9.     return 0;
  10. }

  11. //画正方形:以(x, y)为左上角点,len为边长画方块
  12. int square(int x, int y, int len , u32_t color)
  13. {
  14.     int i, j;

  15.     for(i = 0; i < len; i ++)
  16.     {
  17.         for(j = 0; j < len; j ++)
  18.         {
  19.             fb_one_pixel(x + i, y + j, color);
  20.         }
  21.     }
  22.     return 0;
  23. }

  24. //画圆:以(x, y)为圆心,radius为半径画圆
  25. int circle(int x, int y, int radius, u32_t color)
  26. {
  27.     int i, j;
  28.     printf("x");

  29.     for(i = x -radius; i <= x + radius; i ++)
  30.     {
  31.         for(j = y - radius; j <= y + radius; j ++)
  32.         {
  33.             if((((i - x) * (i - x) + (j - y) * (j - y)) <= (radius * radius)) && i >= 0 && i < 1024 && j >= 0 && j < 768)
  34.             {
  35.                 fb_one_pixel(i, j, color);
  36.             }
  37.         }
  38.     }
  39.     return 0;
  40. }

  41. //画矩形框:以左上角点(x1, y1)和右上角(x2, y2)为基准
  42. int rectangle(int x1, int y1, int x2, int y2, u32_t color)
  43. {
  44.     int i, j;
  45.     
  46.     for(i = x1; i < x2; i ++)
  47.     {
  48.         for(j = y1; j < y2; j ++)
  49.         {
  50.             if(i == x1 || i == x2 - 1 || j == y1 || j == y2 - 1)
  51.             {
  52.                 fb_one_pixel(i, j, color);
  53.             }
  54.         }
  55.     }
  56.     return 0;
  57. }


点击(此处)折叠或打开

  1. //snake.c

  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <time.h>
  6. #include <unistd.h>
  7. #include <sys/ioctl.h>
  8. #include "frame.h"

  9. fbsrc_t fb_v;
  10. int rand_food[2];


  11. int move_one_step(int *sx, int *sy, int x_offset, int y_offset, int s_len)
  12. {
  13.     int i;
  14.     
  15.     // 判断贪吃蛇是否碰到左右墙:碰到墙返回-1
  16.     if(((sx[0] + x_offset) < 100) || ((sx[0] + x_offset + 50) > 900))
  17.     {
  18.         return -1;
  19.     }
  20.     // 判断贪吃蛇是否碰到上下墙:碰到墙返回-1
  21.     if(((sy[0] + y_offset) < 50) || ((sy[0] + y_offset + 50) > 700))
  22.     {
  23.         return -1;
  24.     }

  25.     //覆盖掉蛇身的最后一节
  26.     square(sx[s_len - 1], sy[s_len- 1], 50, BLACK);
  27.     //将因消掉最后一节而覆盖掉的倒数第二个节点的边界框,重新加上
  28.     rectangle(sx[s_len -2], sy[s_len - 2], sx[s_len - 2] + 50, sy[s_len - 2] + 50, RED);

  29.     //消掉蛇尾,添加蛇头,更新存储蛇的数组
  30.     for(i = s_len - 1; i > 0; i --)
  31.     {
  32.         sx[i] = sx[i - 1];
  33.         sy[i] = sy[i - 1];
  34.     }
  35.     sx[0] += x_offset;
  36.     sy[0] += y_offset;

  37.     //在原来蛇头的基础上,添加蛇头
  38.     square(sx[0], sy[0], 50, GREEN);
  39.     rectangle(sx[0], sy[0], sx[0] + 50, sy[0] + 50, RED);

  40.     //此时原来的蛇头变为蛇身的一部分,将其颜色改为蛇身的颜色
  41.     square(sx[1], sy[1], 50, BLUE);
  42.     rectangle(sx[1], sy[1], sx[1] + 50, sy[1] + 50, RED);

  43.     usleep(1000 * 300);

  44.     return 0;
  45. }

  46. int keyhit(void)
  47. {
  48.     int i = 0;

  49.     //将从标准输入读到的数存储到i中。若读到数:返回非0的数;若读不到:返回0
  50.     ioctl(STDIN_FILENO, FIONREAD, &i);

  51.     return i;
  52. }

  53. char get_key(void)
  54. {
  55.     //标准输入中有数:用getchar获取
  56.     if(keyhit() > 0)
  57.        return getchar();

  58.     return 0;
  59. }

  60. //打印贪吃蛇
  61. int print_snake(int *sx, int *sy, int s_len)
  62. {
  63.     int i;

  64.     for(i = 0; i < s_len; i ++)
  65.     {
  66.        if( i == 0)
  67.        {
  68.             square(sx[i], sy[i], 50, GREEN);
  69.             //打印贪吃蛇的头
  70.        }
  71.        else
  72.        {
  73.             square(sx[i], sy[i], 50, BLUE);
  74.             //打印贪吃蛇的蛇身
  75.        }
  76.        rectangle(sx[i], sy[i], sx[i] + 50, sy[i] + 50, RED);
  77.        //给贪吃蛇的每节加边框
  78.     }
  79.     return 0;
  80. }


  81. //擦除贪吃蛇
  82. int erase_snake(int *sx, int *sy, int s_len)
  83. {
  84.     int i;
  85.     
  86.     for(i = 0; i < s_len; i ++)
  87.     {
  88.         square(sx[i], sy[i], 50, BLACK);
  89.         //擦除贪吃蛇的每个节点
  90.     }
  91.     return 0;
  92. }

  93. //重启游戏
  94. int restart_game(int *sx, int *sy, int s_len)
  95. {
  96.     int i = 5;

  97.     //实现贪吃蛇的闪烁
  98.     while(i -- >= 0)
  99.     {
  100.         print_snake(sx, sy, s_len);
  101.         usleep(1000 * 200);
  102.         erase_snake(sx, sy, s_len);
  103.         usleep(1000 * 200);
  104.     }

  105.     memset(fb_v.mem, 0, (fb_v.wide * fb_v.high * fb_v.bpp) / 8);
  106.     //清屏,将mmap映射得到的内存区域清0,即在屏幕上所有的像素点打印黑色
  107.     rectangle(100 - 1, 50 - 1, 900 + 1, 700 + 1, GREEN);
  108.     //给贪吃蛇划定活动区域

  109.     //初始化贪吃蛇的蛇身各节点的坐标
  110.     for(i = 0; i < s_len; i ++)
  111.     {
  112.         sx[i] = 250 - i * 50;
  113.         sy[i] = 300;
  114.     }
  115.     return 0;
  116. }

  117. //判断坐标为(x, y)的点,在不在蛇身上
  118. //若在,返回1;否则,返回0
  119. int is_covery(int *sx, int *sy, int s_len, int x, int y)
  120. {
  121.     int i;

  122.     for(i = 0; i < s_len; i ++)
  123.     {
  124.         if((sx[i] == x) && (sy[i] == y))
  125.         {
  126.             return 1;
  127.         }
  128.     }
  129.     return 0;
  130. }


  131. //随机产生一个食物
  132. int show_food(int *sx, int *sy, int s_len)
  133. {
  134.     int x, y;

  135.     //若为游戏刚启动,第一个产生的食物,不覆盖
  136.     //否则擦除上一个产生的食物,在生成下一个食物
  137.     if(rand_food[0] != 0)
  138.     {
  139.         square(rand_food[0], rand_food[1], 50, BLACK);
  140.     }

  141.     //随机生成一个符合条件的食物坐标
  142.     while(1)
  143.     {
  144.         x = (rand() % 14 + 3) * 50;
  145.         y = (rand() % 11 + 2) * 50;
  146.         
  147.         if(is_covery(sx, sy, s_len, x, y) == 0)
  148.         {
  149.             break;
  150.         }
  151.     }
  152.     //以该生成的坐标,画食物
  153.     rand_food[0] = x;
  154.     rand_food[1] = y;
  155.     square(x, y, 50, GREEN);

  156.     return 0;
  157. }


  158. //贪吃蛇吃食物
  159. int eat_node(int *sx, int *sy, int *s_len)
  160. {
  161.     //若贪吃蛇的头结点的坐标,恰好等于食物坐标,吃食物
  162.     //扩充贪吃蛇的长度,s_len加1(s_len为传入和传出参数),将新扩充的节点附加在贪吃蛇的尾巴处
  163.     if(sx[0] == rand_food[0] && sy[0] == rand_food[1])
  164.     {
  165.         sx[*s_len] = sx[*s_len - 1];
  166.         sy[*s_len] = sy[*s_len - 1];

  167.         //画新扩充的节点
  168.         square(sx[*s_len], sy[*s_len], 50, BLUE);
  169.         rectangle(sx[*s_len], sy[*s_len], sx[*s_len] + 50, sy[*s_len] + 50, RED);

  170.         //贪吃蛇蛇身的长度加1
  171.         *s_len = *s_len + 1;
  172.         return 1;
  173.     }
  174.     return 0;
  175. }


  176. int snake_move(void)
  177. {
  178.     //初始化蛇身各节点的坐标,各节点为宽度为50的正方形
  179.     int sx[20] = {300, 250, 200, 150};
  180.     int sy[20] = {300, 300, 300, 300};
  181.     int s_len = 4;
  182.     
  183.     int step = 50, flag = 0, rand_flag = 0;
  184.     //step为贪吃蛇每次移动的长度,即为蛇身各正方形节点的边长
  185.     //flag为退出标志,按下q时:将flag = 1,退出循环
  186.     //rand_flag为开始游戏时,第一次产生食物的标志。
  187.     //若是刚开始游戏,则立即产生一个食物;否则,间隔产生
  188.     int x_offset = step, y_offset = 0;
  189.     //贪吃蛇每次移动的偏移量,初始化为向左移动

  190.     int rand_space = 40;
  191.     //随机产生食物的间隔,以贪吃蛇移动的次数为间隔
  192.     
  193.     char dict = 'd';
  194.     //贪吃蛇的移动方向

  195.     char new_dict = 0;
  196.     //贪吃蛇的新的移动方向

  197.     long time = 0;
  198.     //贪吃蛇到吃到食物的移动次数
  199.      
  200.     rectangle(100 - 1, 50 - 1, 900 + 1, 700 + 1, GREEN);
  201.     //create a rectangle: by two dot (100, 50) (900, 700)

  202.     system("stty raw -echo");
  203.     //调用此函数:相当于在命令行中输入该命令
  204.     //注意:此时程序的退出不能再按Ctrl + c
  205.     //否则会有麻烦,将不能恢复到标准输入输出的正常状态(必须重启),
  206.     //所以该程序的退出一定要设置退出标志

  207.     //this function is equal to entering the command in the command line.
  208.     //program :q for quit: can not press Ctrl + c
  209.     // the computer will have trouble (restart), if you press Ctrl + c
  210.  
  211.     while(flag == 0)
  212.     {
  213.         time ++;
  214.         if(s_len >= 20)
  215.         {
  216.             break;
  217.         }

  218.         //若为刚启动游戏,则立即产生一个食物,并修改刚启动游戏的标志
  219.         //或若吃到食物,则立即产生一个食物
  220.         if(eat_node(sx, sy, &s_len) == 1 || rand_flag == 0)
  221.         {
  222.             show_food(sx, sy, s_len);
  223.             rand_flag = 1;
  224.         }
  225.         //控制产生食物的间隔
  226.         if(time == rand_space)
  227.         {
  228.             show_food(sx, sy, s_len);
  229.             time = 0;
  230.         }

  231.         //判断是否撞倒墙
  232.         if(move_one_step(sx, sy, x_offset, y_offset, s_len) == -1 || is_covery(sx + 1, sy + 1, s_len - 1, sx[0], sy[0]) == 1)
  233.         {
  234.         /*
  235.             restart_game(sx, sy, s_len);
  236.             dict = 'd';
  237.             new_dict = 0;
  238.             x_offset = step;
  239.             y_offset = 0;
  240.             s_len = 4;
  241.        */
  242.             break;
  243.         }



  244.         //获得新的方向控制
  245.         new_dict = get_key();

  246.         //控制贪吃蛇不能在其方向的反方向上移动
  247.         switch(new_dict)
  248.         {
  249.             case 'a':{ if(dict != 'd') dict = new_dict; break; }
  250.             case 'd':{ if(dict != 'a') dict = new_dict; break; }
  251.             case 'w':{ if(dict != 's') dict = new_dict; break; }
  252.             case 's':{ if(dict != 'w') dict = new_dict; break; }
  253.             case 'q': dict = new_dict;
  254.             default: break;
  255.         }

  256.         //贪吃蛇的实际移动方向
  257.         switch(dict)
  258.         {
  259.             case 'a':{ x_offset = -step; y_offset = 0; break; }
  260.             case 'd':{ x_offset = step; y_offset = 0; break; }
  261.             case 'w':{ x_offset = 0; y_offset = -step; break; }
  262.             case 's':{ x_offset = 0; y_offset = step; break; }
  263.             case 'q': flag = 1; break;
  264.             default: break;
  265.         }

  266.     }

  267.     system("stty cooked echo");
  268.     //恢复正常设置

  269.     return 0;
  270. }


点击(此处)折叠或打开

  1. //main.c

  2. #include <stdio.h>
  3. #include <unistd.h>
  4. #include <unistd.h>
  5. #include "frame.h"

  6. int main(int argc, const char *argv[])
  7. {

  8.     init_data();

  9.     snake_move();

  10.     return 0;
  11. }


阅读(1062) | 评论(0) | 转发(0) |
0

上一篇:如何制作ramdisk _0326

下一篇:快译通 _0317

给主人留下些什么吧!~~