俄罗斯方块编写思路及源码

    技术2022-05-11  10

     

    俄罗斯方块编写思路及源码

      

        顾名思义,俄罗斯方块自然是俄罗斯人发明的。这位牛人叫做阿列克谢·帕基特诺夫(Alexey Pazhitnov) 。

      俄罗斯方块的基本规则:   1、一个用于摆放小型正方形的平面虚拟场地,其标准大小:行宽为10,列高为20,以每个小正方形为单位;   2、一组由4个小型正方形组成的规则图形,共有7种,分别以S、Z、L、J、I、O、T这7个字母的形状来命名;   3、随机地输出方块到场地顶部,以一定的规则进行移动、旋转、下落和摆放,锁定并填充到场地中。每次摆放如果将场地的一行或多行完全填满,则组成这些行的所有小正方形将被消除,并且以此来换取一定的积分。而未被消除的方块会一直累积,并对后来的方块摆放造成各种影响。   4、方块移到区域最下方或是落到其他方块上无法移动时,就会固定在该处。如果未被消除的方块堆放的高度超过场地所规定的最大高度,则游戏结束。

         下面我简单说一下我的思路,理解下面几点会比较清楚一些。

      1,基础:你首先要能画出一个带颜色的方块。举一反三:可以画一个就可以画4个了。(TC中画方块用到了EGAVGA.BGI这个文件)   2,移动:一个方块消失,相邻地方一个方块出现,在视觉上就是移动了。   3,消失:用背景颜色在同样的地方画同样大小的方块。   4,俄罗斯方块:由四个方块组成,方块互相邻接。共有7种俄罗斯方块(S、Z、L、J、I、O、T)。   5,每种俄罗斯方块,又可以旋转,以90度旋转,则每种就有4个旋转的状态。   6,相对坐标:视觉上像素这个单位太小,用方块的大小作为相对坐标的单位。   7,随机产生:使用伪随机函数,参数一般用上系统当前时间,你再随意捏造个四则运算,就会产生出独一无二的随机数了。随机数用于产生新的俄罗斯方块的种类,以及旋转的状态。   8,按键分四种:上、下、左、右。上键:旋转当前的俄罗斯方块;下键:快速下降到底;左键:左移一格;右键:右移一格。   9,旋转:默认按顺时钟旋转。这里用到了一个坐标的转换,当方块旋转时,横坐标与纵坐标要互换,我这里是把数学中的坐标转换公式应用进来了,这是这个游戏中最有难度的部分了。   10,一行填满:一行里面,填充满小方块,此时需要进行记分,并消掉这一行。   11,记分:每消掉一行,进行记分;若同时消掉多行,记分要更多。--这是鼓励一次消多行。   12,结束条件:方块填充到了顶部。

      也就这么多,理解了这些,整个框架也就出来了。

     

     

    源代码如下:

     

    #include <graphics.h> #include <stdio.h> #include <stdlib.h>

    char x,y;/*当前操作的俄罗斯方块的坐标,实际以一个俄罗斯方块的左上角的小方块的坐标表示*/ char str[16]; char num_line_full=0,num_line_old=0;/*消掉的行数,用于计分*/ char type_cur,type_new;/*俄罗斯方块共有7种,以变量type_cur表示*/ char block_status;/*无论如何转动,只有四种状态。以变量block_status来表示。*/ char screen[10][29]={0};/*表示画面内的各个格是否填充。  画面大小:横向可以放下10个方块,纵向可以放下29个方块 */ char block_unit[4][2]={0};/*表示一个俄罗斯方块,其中有4个小方块,每个小方块有坐标(x,y) */ int score1=0,speed=1000;/*speed其实是个延迟时间,可根据CPU的速度调整。更好的方式是使用别的方式进行延时*/

    void draw_unit(char,char,char);  /* 画一个小方块  */          void dl(int); /* 进行延时  */  void draw_block(char);  /* 画一个俄罗斯方块  */                  void set_block_pos(char,char);/*给一个俄罗斯方块的位置赋值*/ void up(void);  /* 按下上键后的操作  */                         void down(void);/* 按下下键后的操作  */    void left(void);/* 按下左键后的操作  */                         void right(void);/* 按下右键后的操作  */    char rotate_x(char,char,char);    /* x 坐标旋转*/         char rotate_y(char,char,char);/* y 坐标旋转*/ char max1(char,char,char);  /*取最大值*/       char min1(char,char,char);/*取最小值*/ void score(void);/* 计算并显示得分  */                    void process_full_line(void);/*处理填满小方块的行*/ void save(void);/* 保存最高分值  */

    main() {      char cycy=0,i,j,b,ci;      initgraph(VGA,VGAHI,"c://tc//egavga.bgi");/*图形模式初始化*/      getch();

         setfillstyle(1,7);/*设置填充模式*/      setcolor(7);/*设置颜色*/      bar(200,10,350,430);/*按设置的模式,画一个长方形*/      setcolor(15);rectangle(198,8,352,432);  x=4;type_cur=0;block_status=0;y=1;  do  {    set_block_pos(type_cur,block_status);/*进行位置赋值*/    draw_block(14);/*画出*/    score();/*记分*/    for(ci=0;ci<10;ci++)/*下降一格的时间里面,提供10次机会,判断是否有按键,并进行相应处理*/    {      if(kbhit()!=0)        b=getch();/*获取按键值*/      else        b=0;

         if(b==13)       break;      switch(b)      {                   case 72: up();    break;/*上键处理*/                   case 75: left();  break;/*左键处理*/                   case 77: right(); break;/*右键处理*/                   case 80: down();  break;/*下键处理*/       }       dl(speed/2);/*通过延时控制速度*/    }

       j=max1(block_unit[3][1],block_unit[1][1],block_unit[2][1]);   if((j>=27)||(screen[block_unit[0][0]][block_unit[0][1]+1]+screen[block_unit[1][0]][block_unit[1][1]+1]+screen[block_unit[2][0]][block_unit[2][1]+1]+screen[block_unit[3][0]][block_unit[3][1]+1]>=1))/*已经触底*/   {    screen[block_unit[0][0]][block_unit[0][1]]=1;screen[block_unit[1][0]][block_unit[1][1]]=1;    screen[block_unit[2][0]][block_unit[2][1]]=1;screen[block_unit[3][0]][block_unit[3][1]]=1;    y=2;    x=5;       do/*随机产生一个新的俄罗斯方块,其形状与上一个不同*/    {     randomize();               type_new=random(100);     type_new%=7;     if(type_new==0)       type_new=7;    }    while(type_cur==type_new);

       type_cur=type_new;    j=0;    process_full_line();/*判断是否能消一行*/   }   else/*未触底,继续下降*/   {      draw_block(0);/*在原位置消失--准备更新位置,再画出*/      y++;    }    for(i=0;i<10;i++)/*在已经经过下降一格的时间之后,判断在顶部是否有块存在,若存在,则game over*/      if(screen[i][0]==1)      {       cycy=1;       getch();       break;      }

      if(cycy==1)   {    cycy=0;    break;   }  }  while(b!=13);

     getch();  closegraph();/* 关闭图形模式 */  save();/* 若创造新记录,保存分值 */  getch(); }

    void draw_unit(char x,char y,char color)/* 画一个小方块  */ {  setfillstyle(1,color);  setcolor(color);  bar(200+x*15,10+y*15,200+(x+1)*15-2,10+(y+1)*15-2); }

    void draw_block(char color)/* 画一个俄罗斯方块  */ {  char i,t;  for(i=0;i<4;i++)     draw_unit(block_unit[i][0],block_unit[i][1],color); }

    void dl(int a)/* 进行延时 ,更好的方式是使用别的方式进行延时,避免完全占用cpu */ {  int r,n;  for(r=0;r<a;r++)   for(n=0;n<30000;n++)   {    n++;    n--;   } }

    char rotate_x(char x0,char y0,char n)/* x 坐标旋转*/ {  char x1,x2,y1,y2;  x1=y0+x-y;     y1=x+y-x0;  if(n==0)     return(x0);  if(n==1)     return(x1);  if(n>=2)  {   x2=y1+x-y;    y2=x+y-x1;   if(n==2)      return(x2);   else   {    x1=y2+x-y;    y1=x+y-x2;    if(n==3)       return(x1);   }  } }

    char rotate_y(char x0,char y0,char n)/* y 坐标旋转*/ {  char x1,x2,y1,y2;  x1=y0+x-y;     y1=x+y-x0;  if(n==0)     return(y0);  if(n==1)   return(y1);  if(n>=2)  {   x2=y1+x-y;    y2=x+y-x1;   if(n==2)      return(y2);   else   {    x1=y2+x-y;    y1=x+y-x2;    if(n==3)       return(y1);   }  } }

    void left()/* 按下左键后的操作  */ {  char j;  set_block_pos(type_cur,block_status);  draw_block(0);/*原位置去掉*/  j=min1(block_unit[3][0],block_unit[1][0],block_unit[2][0]);  if((j>=1)&&(screen[block_unit[0][0]-1][block_unit[0][1]]==0)&&(screen[block_unit[1][0]-1][block_unit[1][1]]==0)&&(screen[block_unit[2][0]-1][block_unit[2][1]]==0)&&(screen[block_unit[3][0]-1]

    [block_unit[3][1]]==0))  {   j--;   x--;  }

     set_block_pos(type_cur,block_status);  draw_block(14);/*新位置画出*/ }

    void right()/* 按下右键后的操作  */  {  char j;  set_block_pos(type_cur,block_status);  draw_block(0);/*原位置去掉*/  j=max1(block_unit[3][0],block_unit[1][0],block_unit[2][0]);  if((j<9)&&(screen[block_unit[0][0]+1][block_unit[0][1]]==0)&&(screen[block_unit[1][0]+1][block_unit[1][1]]==0)&&(screen[block_unit[2][0]+1][block_unit[2][1]]==0)&&(screen[block_unit[3][0]+1]

    [block_unit[3][1]]==0))  {   j++;   x++;  }

     set_block_pos(type_cur,block_status);  draw_block(14);/*新位置画出*/ }

    void set_block_pos(char n,char block_status)/* 给一个俄罗斯方块赋值,含4个块,每个块的坐标(x,y)*/ {      block_unit[0][0]=x;  block_unit[0][1]=y;  n=n%7;  if(n==0)     n=7;         block_status=block_status%4;  switch (n)  {   case 1: block_unit[1][0]=rotate_x(x-1,y,block_status);block_unit[1][1]=rotate_y(x-1,y,block_status);      block_unit[2][0]=rotate_x(x,y+1,block_status);block_unit[2][1]=rotate_y(x,y+1,block_status);      block_unit[3][0]=rotate_x(x,y+2,block_status);block_unit[3][1]=rotate_y(x,y+2,block_status);     break;   case 2:     block_unit[1][0]=rotate_x(x-1,y,block_status);block_unit[1][1]=rotate_y(x-1,y,block_status);      block_unit[2][0]=rotate_x(x,y-1,block_status);block_unit[2][1]=rotate_y(x,y-1,block_status);      block_unit[3][0]=rotate_x(x,y-2,block_status);block_unit[3][1]=rotate_y(x,y-2,block_status);     break;   case 3:     block_unit[1][0]=rotate_x(x-1,y-1,block_status);block_unit[1][1]=rotate_y(x-1,y-1,block_status);      block_unit[2][0]=rotate_x(x,y+1,block_status);block_unit[2][1]=rotate_y(x,y+1,block_status);      block_unit[3][0]=rotate_x(x-1,y,block_status);block_unit[3][1]=rotate_y(x-1,y,block_status);     break;   case 4:     block_unit[1][0]=rotate_x(x-1,y,block_status);block_unit[1][1]=rotate_y(x-1,y,block_status);      block_unit[2][0]=rotate_x(x-1,y+1,block_status);block_unit[2][1]=rotate_y(x-1,y+1,block_status);      block_unit[3][0]=rotate_x(x,y-1,block_status);block_unit[3][1]=rotate_y(x,y-1,block_status);     break;   case 5:     block_unit[1][0]=rotate_x(x-1,y,block_status);block_unit[1][1]=rotate_y(x-1,y,block_status);      block_unit[2][0]=rotate_x(x,y-1,block_status);block_unit[2][1]=rotate_y(x,y-1,block_status);      block_unit[3][0]=rotate_x(x,y+1,block_status);block_unit[3][1]=rotate_y(x,y+1,block_status);     break;   case 6:     block_unit[1][0]=rotate_x(x,y+1,block_status);block_unit[1][1]=rotate_y(x,y+1,block_status);      block_unit[2][0]=rotate_x(x,y-1,block_status);block_unit[2][1]=rotate_y(x,y-1,block_status);      block_unit[3][0]=rotate_x(x,y-2,block_status);block_unit[3][1]=rotate_y(x,y-2,block_status);     break;   case 7:     block_unit[1][0]=rotate_x(x-1,y,block_status);block_unit[1][1]=rotate_y(x-1,y,block_status);      block_unit[2][0]=rotate_x(x,y-1,block_status);block_unit[2][1]=rotate_y(x,y-1,block_status);      block_unit[3][0]=rotate_x(x-1,y-1,block_status);block_unit[3][1]=rotate_y(x-1,y-1,block_status);     break;  } }

    void up()/* 按下上键后的操作  */ {  char j,k;  set_block_pos(type_cur,block_status);  draw_block(0);  block_status++;  block_status=block_status%4;  set_block_pos(type_cur,block_status);  j=min1(block_unit[3][0],block_unit[1][0],block_unit[2][0]);  k=max1(block_unit[3][0],block_unit[1][0],block_unit[2][0]);  if((k<=9)&&(j>=0))  {   if((screen[block_unit[0][0]][block_unit[0][1]]==0)&&(screen[block_unit[1][0]][block_unit[1][1]]==0)&&(screen[block_unit[2][0]][block_unit[2][1]]==0)&&(screen[block_unit[3][0]][block_unit[3][1]]==0))    draw_block(14);      /* not overlap */   else   {                block_status+=3;        block_status%=4;    set_block_pos(type_cur,block_status);    draw_block(14);   }  }  else  {   block_status+=3;       block_status%=4;   set_block_pos(type_cur,block_status);   draw_block(14);  } }

    void down(void)/* 按下下键后的操作  */   {  char j,cy=0;  set_block_pos(type_cur,block_status);  draw_block(0);  j=max1(block_unit[3][1],block_unit[2][1],block_unit[1][1]);

     while(j<28)  {       j=max1(block_unit[3][1],block_unit[1][1],block_unit[2][1]);   if((j>=27)||(screen[block_unit[0][0]][block_unit[0][1]+1]==1)||(screen[block_unit[1][0]][block_unit[1][1]+1]==1)||(screen[block_unit[2][0]][block_unit[2][1]+1]==1)||(screen[block_unit[3][0]][block_unit[3][1]+1]==1))   {    screen[block_unit[0][0]][block_unit[0][1]]=1;    screen[block_unit[1][0]][block_unit[1][1]]=1;      screen[block_unit[2][0]][block_unit[2][1]]=1;    screen[block_unit[3][0]][block_unit[3][1]]=1;      draw_block(14);   /* avoid block disappeared */      process_full_line();    y=2;    x=5;    do    {        randomize();               type_new=random(100);        type_new%=7;        if(type_new==0)       type_new=7;    }    while(type_new==type_cur);        /* avoid the same block appear continually */

       type_cur=type_new;

       j=0;    set_block_pos(type_cur,block_status);    draw_block(14);    cy=1;   }   else   {    dl(speed/4);      set_block_pos(type_cur,block_status);      draw_block(0);      y++;   }   if(cy==1)   {    cy=0;    break;   }   set_block_pos(type_cur,block_status);   draw_block(14);  } }

    void process_full_line(void)/*消掉一行*/ {  char jj,y1,r,x1,y2;  for(jj=0;jj<4;jj++)/*分别判断四个小方块---似乎不需要??下面已经是整屏幕判断了*/  {   for(y1=27;y1>0;y1--)/*从底下判断到顶上。*/   {    r=0;    for(x1=0;x1<10;x1++)/*对于每一行,从左判断到右*/    {     if(screen[x1][y1]==1)/*统计已经填充的小方块的个数*/         r++;    }    if(r==10)/*一行已经填满了*/    {     num_line_full++;     for(x1=0;x1<10;x1++)     {      draw_unit(x1,y1,0);/*使该行消失*/      screen[x1][y1]=0;/*清标志*/     }

        for(y2=y1-1;y2>0;y2--)/*上面的所有块,整体下移一行*/     {      for(x1=0;x1<10;x1++)      {       if(screen[x1][y2]==1)       {        screen[x1][y2]=0;        draw_unit(x1,y2,0);        draw_unit(x1,y2+1,14);        screen[x1][y2+1]=1;       }      }     }    }   }  } }

    char max1(char a,char b,char c)/*取最大值*/   {  char max3;  if(a>=b)            max3=a;  else            max3=b;

            if(max3<c)            max3=c;

            return(max3);   }

    char min1(char a,char b,char c)/*取最小值*/ {  char min;  if(a<=b)     min=a;  else     min=b;    if(min>c)     min=c;

     return(min);   }

    void score(void)/* 计算并显示得分  */ {  char a,b=1;  setcolor(0);  outtextxy(20,20,str);  setcolor(14);  for(a=0;a<num_line_full-num_line_old;a++)     b*=2;  score1+=b-1;  if(b>1)     speed-=2;  num_line_old=num_line_full;  sprintf(str,"Your score:%d.",score1);  outtextxy(20,20,str); }

    void save(void)/* 保存最高分值  */ {  FILE * fp;  int i;  i=0;  fp=fopen("bloscore.txt","rb+");  i=(int)(fgetc(fp))-48;  if(fp=NULL)     i=0;  fclose(fp);

     if(i<score1)  {     fp=fopen("bloscore.txt","wb+");     fprintf(fp,"%d",score1);     fclose(fp);     printf("Your score: %d are the highest!",score1);  }  getch();

    }

     

        编译运行需要一个文件:egavga.bgi,网络上可以下载到,我是存放在我的电脑的 c:/tc 目录下。

        这个程序还不是很完善,只是实现了基本功能,第一次的开始时,需要按一下方向键,最后的退出也有一点小问题,不过作为一个小游戏玩玩还是可以的了。

        有兴趣的朋友帮忙完善一下,加上了通知我一声哦。

    后记:       其实现在来发布贪吃蛇与俄罗斯方块这两个小游戏的源码,已经显不出有什么特别了,因为网络上已经有多个版本了。只是在2001年那时,这两个小游戏却是我的快乐与光荣。当时才学C语言,自己估摸着可以做点东西,就开始做了。都是我一个人编写与调试,在其他人玩乐的时候,是有一点孤单的。做出来后,看别人玩自己编写的游戏,很是自豪,不过却没有人来一起讨论编程中的得失,有些遗憾。现在虽然迟了,发布出来,也是了一个心愿吧。回头看以前编写的程序,格式不够规范,思路也很粗糙,远称不上完善,但那也是我成长过程中的一个脚印啊,与大家分享一下。

        这个游戏是我编写贪吃蛇之后的又一个尝试,本以为也只是几个方块的组合,类似于贪吃蛇,结果费了很大的功夫才编写成型,主要的困难在于边界的处理,需要非常仔细的考虑,否则bug一大堆呀。

      回头重看代码,确实很难看懂。就重新整理了一下,修改了一些变量、函数名称,添加了部分注释,增加一些代码可读性。

     

     

     


    最新回复(0)