以前我自己设计的“俄罗斯方块”,觉得挺有意思,今天贴出来

您所在的位置:网站首页 俄罗斯方块多久出来的 以前我自己设计的“俄罗斯方块”,觉得挺有意思,今天贴出来

以前我自己设计的“俄罗斯方块”,觉得挺有意思,今天贴出来

2024-07-03 09:00| 来源: 网络整理| 查看: 265

“俄罗斯方块”游戏设计          电子科技大学软件学院03级02班 周银辉                                 转载请注明出处

说明:这是一次尝试,一个比较成功的设计,其精彩的算法与漂亮的程序结构足以让人兴奋了。这有别于常规的俄罗斯方块算法,如果你需要常规的实现方法,可以通过这个e_mail:[email protected]索取。

平台说明:开发平台    ms.net 2005 开发语言    C# 2.0

特殊名词说明:3.1,象素坐标(Pixel Coordinate):以显示区域所在控件的Client Rectangle的左上角为坐标原点,一个象素点为单位1的坐标

3.2,网格坐标(Gird Coordinate):如果我们将显示区域分成m×n 的网格,那么其中某一个网格所在(列,行)组成的坐标,我们称之为网格坐标,在程序中网格坐标相关项以Gird或g或G开头

3.3,块(Block):一个小格子(比如游戏中的方块图形是由小格子组成的)称为一个块,将由块来组成游戏图形

3.4,形状(Shape):游戏中的由四个小格子组成的图形称为形状

3.5,基础点(Base Point):用于指示目前形状所在位置的一个(列,行)坐标点,以网格坐标为坐标。后面将详细说明其来历。

3.6,动态方块(Dynamic Block):用于构成运动的形状的Block,共4个

设计思想:4.1,屏幕的组成:     0 ⒈⒉⒊⒋⒌⒍⒎⒏⒐  0  □□□□□□□□□□  1  □□□□□□□□□□  2  □□□□□□□□□□  --------------                        3  □□□□□□□□□□  4  □□□□□□□□□□  5  □□□□□□□□□□  6  □□□□□□□□□□  7  □□□□□□□□□□  8  □□□□□□□□□□  9  □□□□□□□□□□ 10  □□□□□□□□□□ 11  □□□□□□□□□□ 12  □□□□□□□□□□ 13  □□□□□□□□□□ 14  □□□□□□□□□□ 15  □□□□□□□□□□ 16  □□□□□□□□□□ 17  □□□□□□□□□□ 18  □□□□□□□□□□ 19  □□□□□□□□□□ 20  □□□□□□□□□□ 21  □□□□□□□□□□ 22  □□□□□□□□□□-------------- 23  ■■■■■■■■■■屏幕由23行10列的网格组成;其中0~2行:初始的形状将在这里形成然后下落,这三行用户不可见;3~22行:游戏显示区域;23行,其标记已到屏幕底部。

4.2,形状的组成:  每一种形状都是由四个方块组成,比如■■■■由四个方块横向排列而成

4.3,形状的统一:  ■■■■等共19种形状(旋转前后的形状归为不同的形状),虽然在玩游戏时我们会去将各种不同的形状命不同的命(比如“条子”,“方块”等),但在设计游戏是它们却是统一的,它们都是“形状”。这一点是游戏成功的基础。     为了使各种不同的形状达到统一的设计,我设计了如下解决方案:将形状始终放在4×4的格子中,以该4×4格子的第一个格子为“基础点”,只要给出组成形状的四个块相对于该基础点的相对坐标,那么在基础点网格坐标的基础上就能求出各块的网格坐标。★□□□   ★为基础点,形状各块的相对坐标是相对于这个基础点的□□□□□□□□□□□□那么■■■■在其中就如图:其四个方块相对于基础点的网格坐标就为(0,2)(1,2)(2,2)(3,2)□□□□ □□□□■■■■  02122232□□□□  假设基础点的网格坐标是(gX, gY),那么此形状的坐标就为(gX+0,gY+2), (gX+1,gY+2), (gX+3,gY+2), (gX+3,gY+2)我们将用一个int[8]记录下这四个相对坐标值(呵呵,用byte[8]就可以了哈)同理:□□□□□□□□■■□□■■□□  02120313这样,我们只要知道某个形状的相对坐标值数组,就可以轻松地求出它的各方块的排列方式,也就是其形状(样子)

4.4,移动与旋转的统一   从上面我们可以看出形状的移动可以这样来实现: 移动基础点的网格坐标,然后组成形状的四个方块按照其与基础点坐标的相对值而改变网格坐标,则表现为移动。   旋转与移动的原理一样:设旋转前的形状为A,旋转后的形状为B,组成形状A的四个方块按照B(而不是按照A)的相对于基础点坐标的相对值而改变网格坐标,则表现为旋转。比如,□□□□ □□□□■■■■  02122232□□□□  移动: 设其基础点网格坐标为(gX,gY),其各方块当前坐标(gX+0,gY+2), (gX+1,gY+2), (gX+3,gY+2), (gX+3,gY+2)。如果其向左移动一格,那么它的基础了坐标gX-=1; gY=gY; 其各方块移动后坐标 (gX+0,gY+2), (gX+1,gY+2), (gX+3,gY+2), (gX+3,gY+2)。旋转:设其基础点网格坐标为(gX,gY),其各方块当前坐标(gX+0,gY+2), (gX+1,gY+2), (gX+3,gY+2), (gX+3,gY+2)。如果其旋转一次,旋转后的形状如图□■□□ □■□□  10111213□■□□□■□□那么其旋转后的各方块坐标 (gX+1,gY+0), (gX+1,gY+1), (gX+1,gY+2), (gX+1,gY+3)

如果我们将各形状编号,比如■■■■编号0,其旋转90度以后的形状为编号1那么0旋转目标为1,1的旋转目标为0所以所有形状便得到了统一,如图:

           形状编号_相对坐标_旋转后的形状编号

(由于排版的问题,一下小方格都应该是左对齐的)□□□□ □□□□■■■■  0_02122232_1□□□□  □■□□ □■□□  1_10111213_0□■□□□■□□

□□□□■□□□■□□□  2_00010212_3■■□□ 

□□□□□□□□■■■□  3_02122203_4■□□□

□□□□■■□□  □■□□  4_01111213_5□■□□

□□□□□□■□■■■□  5_21021222_2□□□□

□□□□□□□□■■□□■■□□  6_02120313_6

□□□□□■□□■■□□  7_11021203_8■□□□   

□□□□■■□□□■■□  8_01111222_7□□□□

□□□□□■□□□■□□  9_11120313_10■■□□ 

□□□□■□□□■■■□  10_01021222_11□□□□

□□□□□■■□□■□□  11_11211213_12□■□□

□□□□□□□□■■■□  12_02122223_9□□■□

□□□□■□□□■■□□  13_01021213_14□■□□

□□□□□■■□  14_11210212_13■■□□□□□□

□□□□□■□□■■■□  15_11021222_16□□□□

□□□□□■□□□■■□  16_11122213_17□■□□

□□□□□□□□■■■□  17_02122213_18□■□□                                                                                                                                                      

□□□□■□□□■■□□  18_11021213_15  □■□□

数据结构5.1,网格坐标:并没有提供这样一个类或结构,但提供了网格坐标与象素坐标的转换公式:   pCoordinate = gCoordinate*(widthOfGird+distanceOfGird);   象素坐标值 = 网格坐标值×(网格边长+网格间距)

5.2,网格点(GirdPoint):一个结构,它表示一个以网格坐标为坐标的点public struct GirdPoint{  public int X;  public int Y;        //…各种构造函数}

5.3,块(Block):继承于System.Windows.Forms.Label,用它来生成组成形状的块对象public class Block: System.Windows.Forms.Label{  private GirdPoint gLocation;// 网格坐标  //获取或设置 网格坐标值(若设置,那么其象素坐标会自动变化为相应值)         public GirdPoint GLocation     {   get   {    return this.gLocation;   }   set   {    this.gLocation = value;                   //其中Globals.ToPcoordinate()是一个将网格坐标值转换成象素坐标值的函数    this.Location = new Point( Globals.ToPCoordinate(this.gLocation.X),     Globals.ToPCoordinate(this.gLocation.Y));   }  }        //……(各种构造函数)}对块位置的操作只需要指定其网格坐标,其象素坐标会自动变化为相应值

5.4,形状(Shape):一个结构,由它保存游戏中的形状的相关信息public struct Shape{   //获取或设置该形状的索引值   public int Index;   //获取或设置一个数组,其为该形状的相对于基础点的相对网格坐标组   public int[] DCoordinates;  // 获取或设置该形状旋转后的新形状的索引   public int EddiedIndex;   public Shape(int index, int[] dCoordinates, int eddiedIndex)   {       this.Index = index;       this.DCoordinates = dCoordinates;      this.EddiedIndex = eddiedIndex;}

5.5,背景方块数组(BackBlocks):一个Block[,],其维持着23行10列的Block对象,这些对象将作为屏幕的背景,当下落的形状下落到底部时,形状所在位置的背景方块将被点亮,这表现为下落的形状被固定住了

5.6,网格数组(GirdArray):一个int[,],其维持着整个屏幕的背景方块的状态,当该数组中的某个元素值由0变为1时,表示在屏幕中的该位置被某个方块占据,当由1变为0是,表示在屏幕中的该位置被释放而未被占据。被占据表现为屏幕上该位置已经有方块了。

5.7,动态方块数组(DynamicBlocksArray):一个Block[4],其维持着四个Block对象,游戏中的任何运动的形状都是由这四个Block对象变化位置而得到的

5.8,预览网格数组(ViewGirdArray):一个int[,],于GirdArray一样,只不过它维持着屏幕旁边的用于预览下一个即将出项的形状的小屏幕

5.9,预览方块数组(ViewDynamicBlocksArray):一个Block[4],其维持着四个Block对象,游戏中屏幕普遍的小屏幕显示了下一个即将出现的形状,而这个预览形状便是由这四个Block对象变化位置而得到的

5.10,Shapes:一个Shape[19],它维持着游戏中的19个形状    public static readonly Shape[] Shapes = new Shape[19]   {    new Shape(0,new int[8]{0,2,1,2,2,2,3,2},1),    new Shape(1,new int[8]{1,0,1,1,1,2,1,3},0),    new Shape(2,new int[8]{0,0,0,1,0,2,1,2},3),    new Shape(3,new int[8]{0,2,1,2,2,2,0,3},4),    new Shape(4,new int[8]{0,1,1,1,1,2,1,3},5),    new Shape(5,new int[8]{2,1,0,2,1,2,2,2},2),    new Shape(6,new int[8]{0,2,1,2,0,3,1,3},6),    new Shape(7,new int[8]{1,1,0,2,1,2,0,3},8),    new Shape(8,new int[8]{0,1,1,1,1,2,2,2},7),    new Shape(9,new int[8]{1,1,1,2,0,3,1,3},10),    new Shape(10,new int[8]{0,1,0,2,1,2,2,2},11),    new Shape(11,new int[8]{1,1,2,1,1,2,1,3},12),    new Shape(12,new int[8]{0,2,1,2,2,2,2,3},9),    new Shape(13,new int[8]{0,1,0,2,1,2,1,3},14),    new Shape(14,new int[8]{1,1,2,1,0,2,1,2},13),    new Shape(15,new int[8]{1,1,0,2,1,2,2,2},16),    new Shape(16,new int[8]{1,1,1,2,2,2,1,3},17),    new Shape(17,new int[8]{0,2,1,2,2,2,1,3},18),           new Shape(18,new int[8]{1,1,0,2,1,2,1,3},15),              }

功能的实现:6.1,形状的生成:随机生成形状编号,并根据该编号生成相应的形状   设形状编号为indexOfShape,组成形状的四个动态方块已存在DynamicBlockArray中,基础点为BasePoint,所有形状已存在Shapes中void AssembleShape(int indexOfShape){ //重新安排四个动态块的位置以形成新形状   DynamicBlocksArray[0].GLocation = new GirdPoint(                  Shapes[indexOfShape].DCoordinates[0] + BasePoint.X,               Shapes[indexOfShape].DCoordinates[1]+ BasePoint.Y);

   DynamicBlocksArray[1].GLocation = new GirdPoint(    Shapes[indexOfShape].DCoordinates[2] + asePoint.X,               Shapes[indexOfShape].DCoordinates[3]+ BasePoint.Y);

   DynamicBlocksArray[2].GLocation = new GirdPoint(              Shapes[indexOfShape].DCoordinates[4] + BasePoint.X,              Shapes[indexOfShape].DCoordinates[5] + BasePoint.Y);

   DynamicBlocksArray[3].GLocation = new GirdPoint(       Shapes[indexOfShape].DCoordinates[6] + BasePoint.X,        Shapes[indexOfShape].DCoordinates[7] + BasePoint.Y);}

6.2,形状的移动,先试探能否向指定方向移动如果能,那么移动,否则不移动。试探,由于左右下三个方向移动的本质是一样的,所以它们可以由统一的函数来实现。移动,则是通过移动基础点位置,然后根据基础点位置重新安排一下四个动态方块的位置来实现,即,移动基础点,然后调用一次上面的AssembleShape函数。     试探:设当前形状编号为indexOfShape,arg参数指定要试探的方向 ’L’,’R’,’D’ 分别为左,右,下;基础点为BascPoint,所有形状已存再Shapes中,CountOfTier为屏幕网格的列数。取得其试探位置的网格坐标,如果该位置已经超出屏幕或该位置已经被其他方块占据,则试探的位置不可达,如果组成形状的四个方块中有一个方块的试探位置不可达,那么试探函数返回false,说明不可向指定方向移动private static bool canMove(int indexOfShape, char arg){ try {     GirdPoint tempBasePoint;

     switch(arg)  {   case 'L':       tempBasePoint = new GirdPoint(BasePoint.X-1,BasePoint.Y);      break;   case 'R':       tempBasePoint = new GirdPoint(BasePoint.X+1, BasePoint.Y);      break;   case 'D':    tempBasePoint = new GirdPoint(BasePoint.X, BasePoint.Y+1);    break;           case 'E'://不移动,用于判断能否旋转,                       //与判断移动不同的是传入的indexOfShape,                       //判断旋转用的是旋转后的形状的索引,而移动用的是当前的                    tempBasePoint = BasePoint;                  break;   default:    MessageBox.Show("错误的参数"+arg+"\n应该为'L'或'R'或'D'");    return false;  }      int gX0 = Shapes[indexOfShape].DCoordinates[0]+tempBasePoint.X;  int gY0 = Shapes[indexOfShape].DCoordinates[1]+tempBasePoint.Y;  int i =  GirdArray[gY0,gX0];               int gX1 = Shapes[indexOfShape].DCoordinates[2]+tempBasePoint.X;  int gY1 = Shapes[indexOfShape].DCoordinates[3]+tempBasePoint.Y;  int j = GirdArray[gY1,gX1];

  int gX2 = Shapes[indexOfShape].DCoordinates[4]+tempBasePoint.X;  int gY2 = Shapes[indexOfShape].DCoordinates[5]+tempBasePoint.Y;  int m = GirdArray[gY2,gX2];

  int gX3 = Shapes[indexOfShape].DCoordinates[6]+tempBasePoint.X;  int gY3 = Shapes[indexOfShape].DCoordinates[7]+tempBasePoint.Y;  int n = GirdArray[gY3,gX3];

  //i,j,m,n为其即将到达的新位置的网格值,若为1,说明该网格已被占据    if(gX0= CountOfTier ||  i == 1 ||       gX1= CountOfTier || j == 1 ||   gX2=  CountOfTier || m == 1 ||   gX3= CountOfTier || n == 1)  {   return false;  }      }    catch    {     return false;    }

    return true;}     移动:移动基础点,并重新组成形状,比如左移,void ToLeft(int indexOfShape){    if(canMove(indexOfShape,’L’)    {  BasePoint = new GirdPoint(BasePoint.X-1, BasePoint.Y);  AssembleShape(indexOfShape);    }}

6.3,自动下落,设一个计时器timer,其每隔一定时间调用一次ToDown函数void ToDown(int indexOfShape){    if(canMove(indexOfShape,’D’)    {  BasePoint = new GirdPoint(BasePoint.X, BasePoint.Y+1);  AssembleShape(indexOfShape);    }    else    {        //其他,比如固定下落的形状,判断得分,判断游戏是否已经结束等等    }}

6.4,下落形状的固定,当试探到不能下落时,就固定住当前形状。其实组成形状的四个块并没有被固定住,而是当它们当前位置对应的四个背景方块被点亮后,它们离开了当前位置而到屏幕的顶部(0~2行,不可见)组合新形状去了。对应的四个背景方块被点亮表现为形状被固定void FixShape(){ for(int i=0; i



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3