C# 如何制作俄罗斯方块克隆?

C# 如何制作俄罗斯方块克隆?,c#,data-structures,software-design,C#,Data Structures,Software Design,我正在用XNAC#编写一个俄罗斯方块克隆,不确定从高层次上处理游戏数据结构方面的最佳方法 我对碰撞检测、旋转、动画等方面完全满意。我需要知道的是存储“丢弃的块”的最佳方法,即不再受玩家控制的块 我认为每个Tetromino块都应该存储在自己的类中,该类由4x4数组组成,这样块就可以轻松地旋转。问题是如何将tetromino的最终位置存储到游戏网格中,然后将tetromino切割成单独的块(对于每个单元格),然后设置主游戏网格的相应位置以容纳这些相同的块,然后在tetromino到达其最终位置后将

我正在用XNAC#编写一个俄罗斯方块克隆,不确定从高层次上处理游戏数据结构方面的最佳方法

我对碰撞检测、旋转、动画等方面完全满意。我需要知道的是存储“丢弃的块”的最佳方法,即不再受玩家控制的块

我认为每个Tetromino块都应该存储在自己的类中,该类由4x4数组组成,这样块就可以轻松地旋转。问题是如何将tetromino的最终位置存储到游戏网格中,然后将tetromino切割成单独的块(对于每个单元格),然后设置主游戏网格的相应位置以容纳这些相同的块,然后在tetromino到达其最终位置后将其消失。也许我的方法有一些缺点

我是否应该为主游戏网格创建一个10x20矩阵,然后存储?或者我应该使用堆栈或队列以某种方式存储丢弃的块。或者有更好的方法/数据结构来存储东西

我相信我的方法会奏效,但我想看看是否有人知道更好的方法,或者我的方法是否足够好


另外,不是家庭作业,这将是我作品集的一个项目。谢谢。

请记住,前一位模糊C代码竞赛的获胜者在不到512字节的模糊C代码中实现了一款相当不错的俄罗斯方块游戏(适用于BSD unix上的VT100终端):

long h[4];t(){h[3]-=h[3]/3000;setitimer(0,h,0);}c,d,l,v[]={(int)t,0,2},w,s,I,K
=0,i=276,j,k,q[276],Q[276],*n=q,*m,x=17,f[]={7,-13,-12,1,8,-11,-12,-1,9,-1,1,
12,3,-13,-12,-1,12,-1,11,1,15,-1,13,1,18,-1,1,2,0,-12,-1,11,1,-12,1,13,10,-12,
1,12,11,-12,-1,1,2,-12,-1,12,13,-12,12,13,14,-11,-1,1,4,-13,-12,12,16,-11,-12,
12,17,-13,1,-1,5,-12,12,11,6,-12,12,24};u(){for(i=11;++i<264;)if((k=q[i])-Q[i]
){Q[i]=k;if(i-++I||i%12<1)printf("\033[%d;%dH",(I=i)/12,i%12*2+28);printf(
"\033[%dm  "+(K-k?0:5),k);K=k;}Q[263]=c=getchar();}G(b){for(i=4;i--;)if(q[i?b+
n[i]:b])return 0;return 1;}g(b){for(i=4;i--;q[i?x+n[i]:x]=b);}main(C,V,a)char*
*V,*a;{h[3]=1000000/(l=C>1?atoi(V[1]):2);for(a=C>2?V[2]:"jkl pq";i;i--)*n++=i<
25||i%12<2?7:0;srand(getpid());system("stty cbreak -echo stop u");sigvec(14,v,
0);t();puts("\033[H\033[J");for(n=f+rand()%7*4;;g(7),u(),g(0)){if(c<0){if(G(x+
12))x+=12;else{g(7);++w;for(j=0;j<252;j=12*(j/12+1))for(;q[++j];)if(j%12==10){
for(;j%12;q[j--]=0);u();for(;--j;q[j+12]=q[j]);u();}n=f+rand()%7*4;G(x=17)||(c
=a[5]);}}if(c==*a)G(--x)||++x;if(c==a[1])n=f+4**(m=n),G(x)||(n=m);if(c==a[2])G
(++x)||--x;if(c==a[3])for(;G(x+12);++w)x+=12;if(c==a[4]||c==a[5]){s=sigblock(
8192);printf("\033[H\033[J\033[0m%d\n",w);if(c==a[5])break;for(j=264;j--;Q[j]=
0);while(getchar()-a[4]);puts("\033[H\033[J\033[7m");sigsetmask(s);}}d=popen(
"stty -cbreak echo stop \023;cat - HI|sort -rn|head -20>/tmp/$$;mv /tmp/$$ HI\
;cat HI","w");fprintf(d,"%4d on level %1d by %s\n",w,l,getlogin());pclose(d);}
long h[4];t(){h[3]=h[3]/3000;setitimer(0,h,0);}c,d,l,v[]={(int)t,0,2},w,s,I,K
=0,i=276,j,k,q[276],q[276],*n=q,*m,x=17,f[]={7,-13,-12,1,8,-11,-12,-1,9,-1,1,
12,3,-13,-12,-1,12,-1,11,1,15,-1,13,1,18,-1,1,2,0,-12,-1,11,1,-12,1,13,10,-12,
1,12,11,-12,-1,1,2,-12,-1,12,13,-12,12,13,14,-11,-1,1,4,-13,-12,12,16,-11,-12,
12,17,-13,1,-1,5,-12,12,11,6,-12,12,24};u(){for(i=11;++i2?V[2]:“jkl pq”;i;i-)*n++=i<

25 | | i%12这闻起来像是家庭作业,但我对俄罗斯方块的面向对象方法的看法是,每个方块都是一个对象,“块”(tetrominos)和网格本身都是相同方块对象的集合

块对象管理下落方块的旋转和位置,网格控制柄显示这些方块并删除已完成的行。每个方块都有与其相关联的颜色或纹理,这些颜色或纹理将由其来源的原始块对象提供,但网格底部的方块将没有其他指示他们曾经是同一个原始街区的一部分

更详细地说,当您创建一个新的块对象时,它会在网格上创建一组具有相同颜色/纹理的4个正方形。网格管理它们的显示。因此,当块到达底部时,您只需忘记块,并且这些正方形仍被网格引用


旋转和放置是一个块只需要处理的操作,只有一个块需要处理它的四个正方形(尽管它需要能够查询网格以确保旋转可以适应).

一旦一个块静止不动,就没有什么能将它与现在静止不动的任何其他块区分开来。在这方面,我认为最有意义的是将整个网格存储为矩阵,其中每个正方形要么被填充,要么不被填充(如果是,还包括块的颜色)


我觉得矩阵有很多优点。它可以使碰撞检测变得简单(无需与多个对象进行比较,只需比较矩阵上的位置)。将其存储为矩阵还可以更容易地确定何时创建了完整的线条。除此之外,当线条消失时,您不必担心拼接固定的俄罗斯方块。当线条消失时,您可以一下子将整个矩阵向下移动。

我决不是俄罗斯方块专家,但正如您所描述的10x20《黑客帝国》对我来说似乎是个自然的选择

当需要检查是否完成一行并处理它时,这将非常容易。只需在2d数组上迭代,查看每个位置的布尔值,看看它们加起来是否达到10个块位置


然而,如果有一行完成了,你将需要手动清理。必须将所有内容向下移动。但归根结底,这并不是什么大问题。

使用数组将是处理俄罗斯方块最简单的方法。在屏幕上看到的内容和内存中使用的结构之间有直接的关联。使用stack/排队将是一种过激行为,而且不必要地复杂

您可以有两个下落块的副本。一个用于显示(Alpha),另一个用于移动(Beta)

你需要一个像这样的结构


class FallingBlock
{
  int pos_grid_x;
  int pos_grid_y;
  int blocks_alpha[4][4];
  int blocks_beta[4][4];

  function movedDown();
  function rotate(int direction();
  function checkCollision();
  function revertToAlpha();
  function copyToBeta()
};
将移动或旋转_beta阵列,并对照电路板检查碰撞。如果发生碰撞,将其还原为_alpha,如果没有,将_beta复制到_alpha

如果movedDown()上发生冲突,则块的生命结束,必须将_alpha网格复制到游戏板上,并删除FallingBlock对象

董事会当然必须是另一种结构,如:


class Board
{
  int gameBoard[10][20];

  //some functions go here
}
我用int来表示一个块,每个值(比如1,2,3)表示不同的纹理或颜色(0表示一个空白点)


一旦区块成为游戏板的一部分,它就只需要显示纹理/颜色标识符。

事实上,我几天前才这么做,除了在WPF而不是XNA中。下面是我所做的:

编辑: 我对“块”的定义似乎与其他人不同。我对块的定义是组成一个Tetromino的4个细胞中的一个,而实际的Tetromino本身就是一个片段

将一个块作为具有X、Y坐标和颜色的结构(后来我添加了一个bool IsSet来指示它是在浮动块中还是在实际板上,但这只是因为我想从视觉上区分它们)

作为块上的方法,我有左、右、下和旋转(块中心),它返回一个新的移位块。这允许我旋转
     . 4 5 6 7 8 .
  .
  19     # # #
  20       #
  .   
 t_shape = [ [6,19], [5,19], [7,19], [6,20] ]
def rotate( shape ):
    X=0      # for selecting the X and Y coords
    Y=1

    # get the middle block
    middle = shape[0]   

    # work out the coordinates of the other blocks relative to the
    # middle block
    rel = []
    for coords in shape:
        rel.append( [ coords[X]-middle[X], coords[Y]-middle[Y] ] )

    # now rotate 90-degrees; x,y = -y, x
    new_shape = []
    for coords in rel:
        new_shape.append( [ middle[X]-coords[Y], middle[Y]+coords[X] ] )

    return new_shape
    new_t_shape = rotate( t_shape )

    new_t_shape
    [[6, 19], [6, 18], [6, 20], [5, 19]]
     . 4 5 6 7 8 .
  .
  18       #
  19     # #
  20       #
  .   
public class Tetromino 
{
    // Block is composed of a Point called Position and the color
    public Block[] Blocks { get; protected internal set; }

    // Constructors, etc.

    // Rotate the tetromino by 90 degrees, clock-wise
    public void Rotate() 
    {
        Point middle = Blocks[0].Position;
        List<Point> rel = new List<Point>();
        foreach (Block b in Blocks)
            rel.Add(new Point(b.Position.x - middle.x, b.Position.y - middle.y));

        List<Block> shape = new List<Block>();
        foreach (Point p in rel)
            shape.Add(new Block(middle.x - p.y, middle.y + p.x));

        Blocks = shape.ToArray();
    }

    public void Translate(Point p)
    {
        // Block Translation: Position+= p; 
        foreach (Block b in Blocks)
            b.Translate(p);
    }
}