Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5787286
  • 博文数量: 409
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 8273
  • 用 户 组: 普通用户
  • 注册时间: 2013-10-23 19:15
个人简介

qq:78080458 学习交流群:150633458

文章分类

全部博文(409)

文章存档

2019年(127)

2018年(130)

2016年(20)

2015年(60)

2014年(41)

2013年(31)

分类: 嵌入式

2015-06-08 17:48:36

俄罗斯方块
一、设置控制台
1、更改屏幕的背景色,字体颜色
    printf("\33[%dm", i);
    30<= i <=37  设置字体颜色
    30黑,31红,32绿,33黄,34蓝,35紫,36深绿,37白
    40<= i <=47  设置背景颜色
    40黑,41红,42绿,43黄,44蓝,45紫,46深绿,47白

2、任意指定屏幕的输出坐标
    printf("\33[x;yH");  将输出坐标定位在第x行,第y列
    printf("\33[%d;%dH", x,y);  将输出坐标定位在第x行,第y列
二、菜单
    1、边框
    游戏区宽48,高31;辅助区宽27,高31
    游戏区起始坐标(2,3);辅助区起始坐标(2,53)(18,53)
    2、开始
    用空格和[]描出一个fight单词
    3、结束
    用空格和[]描出一个game over单词

三、绘制方块
    1、一个小方格
    由于一个字符在屏幕上占的空间是一个长方形,所以用[]来表示一个方格
    2、俄罗斯方块
    总共有7个类型的方块,每个方块有4种摆放的方式(可以旋转)
    一个方块最多占用4个小方格
    用4x4的矩阵来表示一个俄罗斯方块,矩阵中为1的地方,绘制[]
        {0, 0, 0, 0}                     
        {0, 0, 1, 0}    ----->          []
        {0, 0, 1, 1}                    [][]  
        {0, 0, 1, 0}                    []
    3、用一个三位数组表示所有的俄罗斯方块
    int shape[i][j][k]
    i表示方块的类型,j表示方块摆放的方式,k将4x4矩阵变为一维数组
    将上面的二维数组变成一维的{0,0,0,0, 0,0,1,0, 0,0,1,0, 0,0,1,0}
    4、起始坐标
    绘制方块从4x4矩阵的左下角开始,制定其坐标为row、col
    俄罗斯方块中小方格的坐标rrow = row + j/4-3   ccol = ccol + (i%4)*2


四、 自动下落
    1、内核定时器
    setitimer(int which, const struct itimerval *val, struct itimerval *oldval)
    参数which:
        ITIMER_REAL 用系统实时的时间计算,时间减1,减到0发送SIGALRM信号
        ITIMER_VIRTUAL 当前进程用户态计数, 计数完成发送SIGVTALRM信号
        ITIMER_PROF 用户态和内核态同时计数,计数完成发送SIGPROF信号
    参数val:
        struct itimerval{
        struct timeval it_interval;  //定时时间
        struct timeval it_value; //定时器开始执行的时间
        }
        struct timeval{
        long second; //秒
        long usec; //微妙
        }
        参数oldval:保存旧的状态


        struct itimerval val = {
        {0,500000},   //0.5秒的定时时间
        {1,0}  //1s后开启定时器
        };
        setitimer{ITIMER_REAL, &val, NULL};
    2、信号捕捉
        sigaction(int signo, struct sigaction *act, struct sigaction *oldact);
        参数signo: 要捕捉的信号
        参数act:为信号设置动作
        参数oldact: 保存旧的状态,NULL就是不保存


        struct sigaction{
        void (*sa_handler)(int); //信号执行的函数
        sigset_t mask; //信号执行过程中要屏蔽的其他信号
        }


        struct sigaction act;
        act.sa_handler = self_down;
        sigaction(SIGALRM, &act, NULL);


五、 响应键盘
    1、设置终端,键盘可以输入,但是不能在屏幕上显示输入的字符,否则影响游戏画面。
    struct termios old, new;

    tcgetattr(0, &old); //保存旧的状态
    tcgetattr(0, &new);     //获取旧的状态

    new.c_lflag = new.c_lflag & ~(ICANON|ECHO);  //设置屏幕不显示输入的字符
    new.c_cc[VTIME] = 0;
    new.c_cc[VMIN] = 1;

    tcsetattr(0, TCSANOW, &new);  //修改终端的属性,立即生效

    2、响应键盘输入

点击(此处)折叠或打开

  1.         while(1)
  2.         {    
  3.             c = getchar();
  4.             switch(c)
  5.             {
  6.                 case 'a':
  7.                     left(); //左移,列-2,因为一个小方格占2列
  8.                     break;
  9.                 case 'd':
  10.                     right();    //右移,列+2
  11.                     break;
  12.                 case 'w':
  13.                     change(); //逆时针旋转
  14.                     break;
  15.                 case 's':
  16.                     self_down(1); //自由下落 row+1
  17.                     break;
  18.                 default:
  19.                     break;
  20.             }
  21.         }

    3、设置标志位

点击(此处)折叠或打开

  1. 方块降落到最后,需要用以个二维数组来标志,数组中为1的地方证明有方格
  2.         int flag[30][24];
  3.         void set_flag()
  4.         {
  5.             int i;
  6.             int row, col;

  7.             for(i=0; i<16; i++)
  8.             {
  9.                 if(shape[box.sh][box.num][i] == 1)
  10.                 {
  11.                     row = box.row + i/4 -3 -2;
  12.                     col = (box.col + (i%4)*2 + 1)/2-2;
  13.                     flag[row][col] = 1;
  14.                 }
  15.             }
  16.         }

    4、边界判断
        1)、下落判断

点击(此处)折叠或打开

  1. //判断能有下落,当对应方格的下一行位置有东西,那么就不能下落来了
  2.             //比如现在方格在8行9列,那么如果9行9列有东西(判断标志位),那么
  3.             //方格就不能在下落了
  4.             int is_down()
  5.             {
  6.                 int err = 1;
  7.                 int i;

  8.                 if(box.row >= 31)    //第一次超过31行就不能下落,那是最低的边界
  9.                 {
  10.                     err = 0;
  11.                     return err;
  12.                 }

  13.                 for(i = 0; i < 16; i++)    
  14.                 {
  15.                     if(shape[box.sh][box.num][i] == 1)
  16.                     {
  17.                         if(flag[box.row + i/4 -3 -2 + 1][(box.col+i%4*2+1)/2-2] == 1)
  18.                         {
  19.                             err = 0;
  20.                             return err;
  21.                         }
  22.                     }
  23.                 }

  24.                 return err;
  25.             }
        2)、左移判断

点击(此处)折叠或打开

  1. //首先要获取大方块中的最左边的列
  2.             int get_left()
  3.             {
  4.                 int col[4];
  5.                 int i, j, temp;

  6.                 for(i=0, j=0; i<16; i++)
  7.                 {
  8.                     if(shape[box.sh][box.num][i] == 1)    //获取方块中小方格占的列
  9.                     {    
  10.                         col[j] = box.col + (i%4)*2;
  11.                         j++;
  12.                     }
  13.                 }

  14.                 for(i=1; i<4; i++)
  15.                 {
  16.                     if(col[0] > col[i])        //求出列中最小的,也就是最左边的列
  17.                     {    
  18.                         temp = col[0];
  19.                         col[0] = col[i];
  20.                         col[i] = temp;
  21.                     }
  22.                 }

  23.                 return col[0];
  24.             }

  25.             int is_left()                //判断能否左移
  26.             {
  27.                 int err = 1;
  28.                 int i;

  29.                 int col = get_left();
  30.                 if(col == 3)            //最左边的列是第3列,移动到第3列就不能左移了
  31.                     return 0;


  32.                 //如果左边有方格,也不能左移,通过标志位来判断
  33.                 for(i = 0; i < 16; i++)    
  34.                 {
  35.                     if(shape[box.sh][box.num][i] == 1)
  36.                     {
  37.                         if(flag[box.row + i/4 -3 -2][(box.col+i%4*2+1)/2-2-1] == 1)
  38.                         {
  39.                             err = 0;
  40.                             return err;
  41.                         }
  42.                     }
  43.                 }

  44.                 return err;
  45.             }
        3)、右移判断

点击(此处)折叠或打开

  1. //首先要获取大方块中的最右边的列
  2.             int get_right()
  3.             {
  4.                 int col[4];
  5.                 int i, j, temp;

  6.                 for(i=0, j=0; i<16; i++)
  7.                 {
  8.                     if(shape[box.sh][box.num][i] == 1)    //获取方块中小方格占的列
  9.                     {    
  10.                         col[j] = box.col + (i%4)*2;
  11.                         j++;
  12.                     }
  13.                 }

  14.                 for(i=1; i<4; i++)
  15.                 {
  16.                     if(col[0] < col[i])        //求出列中最大的,也就是最右边的列
  17.                     {    
  18.                         temp = col[0];
  19.                         col[0] = col[i];
  20.                         col[i] = temp;
  21.                     }
  22.                 }

  23.                 return col[0];
  24.             }

  25.             int is_right()                //判断能否右移
  26.             {
  27.                 int err = 1;
  28.                 int i;

  29.                 int col = get_right();
  30.                 if(col == 3)            //最右边的列是第49列,移动到第49列就不能右移了
  31.                     return 0;


  32.                 //如果右边有方格,也不能右移,通过标志位来判断
  33.                 for(i = 0; i < 16; i++)    
  34.                 {
  35.                     if(shape[box.sh][box.num][i] == 1)
  36.                     {
  37.                         if(flag[box.row + i/4 -3 -2][(box.col+i%4*2+1)/2-2+1] == 1)
  38.                         {
  39.                             err = 0;
  40.                             return err;
  41.                         }
  42.                     }
  43.                 }

  44.                 return err;
  45.             }
        4)、旋转判断

点击(此处)折叠或打开

  1. 旋转依赖4x4大方格,在4x4大方格内有东西到地方都不能旋转
  2.             int is_change()
  3.             {
  4.                 int err=1;
  5.                 int i;

  6.                 for(i=0; i<16; i++)
  7.                 {
  8.                     if(flag[box.row+i/4-3-2+1][(box.col+1+i%4*2)/2-2] == 1)
  9.                     {
  10.                         err = 0;
  11.                         return err;
  12.                     }
  13.                     if(box.col<3)
  14.                     {
  15.                         err = 0;
  16.                         return err;
  17.                     }
  18.                 }

  19.                 return err;
  20.             }


六、消行
    1、判断是否需要消行,获取需要消的行号

点击(此处)折叠或打开

  1. 在flag数组中,如果有一行全部为1,那么就需要消行了
  2.         int is_clean()
  3.         {
  4.             int err=0, fg=1;
  5.             int num=0;

  6.             int i,j;
  7.             for(i=0; i<30; i++)
  8.             {
  9.                 fg = 1;                    //假设这一行能消去
  10.                 for(j=0; j<24; j++)
  11.                 {
  12.                     if(flag[i][j]==0)    //如果这一行中有一个0,那么就不能消去
  13.                     {
  14.                         fg = 0;    
  15.                         break;
  16.                     }
  17.                 }
  18.                 if(fg == 1)
  19.                 {
  20.                     clean_row[num] = i;    //如果能消行,那么需要记录行号
  21.                     num++;
  22.                     err = 1;
  23.                 }
  24.             }        

  25.             return err;
  26.         }
    2、记录以前的状态,设值新的状态

点击(此处)折叠或打开

  1. void swap()
  2.         {
  3.             int i,j,k;                    //old_flag记录以前的状态
  4.             for(i=0; i<30; i++)
  5.                 for(j=0; j<24; j++)
  6.                     old_flag[i][j] = flag[i][j];


  7.             for(k=0; k<4; k++)            //更新flag,消行后,上面的行要掉下来
  8.                 for(i=0; i<clean_row[k]; i++)
  9.                     for(j=0; j<24; j++)
  10.                     {
  11.                         flag[i+1][j] = old_flag[i][j];
  12.                     }
  13.         }
    3、重新画图

点击(此处)折叠或打开

  1.         void redraw()
  2.         {
  3.             int i,j;

  4.             //将以前的图形全部擦掉
  5.             for(i=0; i<30; i++)        
  6.             {
  7.                 for(j=0; j<24; j++)
  8.                 {
  9.                     if(old_flag[i][j]==1)
  10.                         single_box(i+2, (j+1)*2+1, 0);
  11.                 }
  12.             }

  13.             //绘制新的图形
  14.             for(i=0; i<30; i++)
  15.             {
  16.                 for(j=0; j<24; j++)
  17.                 {
  18.                     if(flag[i][j]==1)
  19.                         single_box(i+2, (j+1)*2+1, 1);
  20.                 }
  21.             }
  22.         }



阅读(14231) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~