俄罗斯方块片段旋转算法

use*_*826 45 algorithm rotation tetris

用于表示和旋转俄罗斯方块游戏的最佳算法(和解释)是什么?我总是发现片段旋转和表示方案令人困惑.

大多数俄罗斯方块游戏似乎在每次轮换时都使用了一个天真的"重制阵列块":

http://www.codeplex.com/Project/ProjectDirectory.aspx?ProjectSearchText=tetris

但是,有些人使用预先构建的编码数字和位移来表示每个部分:

http://www.codeplex.com/wintris

有没有一种方法可以使用数学来做到这一点(不确定哪种方法适用于基于单元的电路板)?

Too*_*the 31

形状有限,所以我会使用固定的表格而不进行计算.这节省了时间.

但是有旋转算法.

选择一个中心点并旋转pi/2.

如果一块的块从(1,2)开始,它顺时针移动到(2,-1)和(-1,-2)和(-1,2).将其应用于每个块并旋转该块.

每个x是前一个y,每个y是前一个x.这给出了以下矩阵:

[  0   1 ]
[ -1   0 ]
Run Code Online (Sandbox Code Playgroud)

对于逆时针旋转,请使用:

[  0  -1 ]
[  1   0 ]
Run Code Online (Sandbox Code Playgroud)

  • 我同意这是一个正确的答案,但我认为它没有详细说明如何在算法上完成这个.请参阅下面的答案,了解更少数学倾向的信息. (11认同)

Jas*_*son 24

当我试图弄清楚旋转对我的俄罗斯方块游戏有多大作用时,这是我在堆栈溢出时发现的第一个问题.虽然这个问题很老,但我认为我的输入会帮助其他人试图通过算法来解决这个问题.首先,我不同意硬编码每个部分和旋转将更容易.Gamecat的答案是正确的,但我想详细说明.以下是我用来解决Java中的旋转问题的步骤.

  1. 对于每个形状,确定其原点.我使用此页面图表上的点来指定我的原点.请记住,根据您的实现,您可能必须在每次用户移动部件时修改原点.

  2. 旋转假定原点位于点(0,0),因此您必须先转换每个块才能旋转.例如,假设您的原点当前位于点(4,5).这意味着在旋转形状之前,每个块必须在x坐标中平移-4,在y坐标中平移-5以相对于(0,0).

  3. 在Java中,典型的坐标平面从左上角的点(0,0)开始,然后向右和向下增加.为了在我的实现中补偿这一点,我在旋转之前将每个点乘以-1.

  4. 以下是我用来计算逆时针旋转后的新x和y坐标的公式.有关这方面的更多信息,我会查看旋转矩阵上的维基百科页面.x'和y'是新坐标:

    x'= x*cos(PI/2)-y*sin(PI/2)和y'= x*sin(PI/2)+ y*cos(PI/2).

  5. 最后一步,我刚按相反的顺序完成了第2步和第3步.所以我再次将结果乘以-1,然后将块转换回原始坐标.

以下代码对我(在Java中)有用,可以了解如何使用您的语言进行操作:

public synchronized void rotateLeft(){

    Point[] rotatedCoordinates = new Point[MAX_COORDINATES];

    for(int i = 0; i < MAX_COORDINATES; i++){

        // Translates current coordinate to be relative to (0,0)
        Point translationCoordinate = new Point(coordinates[i].x - origin.x, coordinates[i].y - origin.y);

        // Java coordinates start at 0 and increase as a point moves down, so
        // multiply by -1 to reverse
        translationCoordinate.y *= -1;

        // Clone coordinates, so I can use translation coordinates
        // in upcoming calculation
        rotatedCoordinates[i] = (Point)translationCoordinate.clone();

        // May need to round results after rotation
        rotatedCoordinates[i].x = (int)Math.round(translationCoordinate.x * Math.cos(Math.PI/2) - translationCoordinate.y * Math.sin(Math.PI/2)); 
        rotatedCoordinates[i].y = (int)Math.round(translationCoordinate.x * Math.sin(Math.PI/2) + translationCoordinate.y * Math.cos(Math.PI/2));

        // Multiply y-coordinate by -1 again
        rotatedCoordinates[i].y *= -1;

        // Translate to get new coordinates relative to
        // original origin
        rotatedCoordinates[i].x += origin.x;
        rotatedCoordinates[i].y += origin.y;

        // Erase the old coordinates by making them black
        matrix.fillCell(coordinates[i].x, coordinates[i].y, Color.black);

    }
    // Set new coordinates to be drawn on screen
    setCoordinates(rotatedCoordinates.clone());
}
Run Code Online (Sandbox Code Playgroud)

这种方法就是将形状向左旋转所需的全部内容,结果比定义每个形状的每个旋转要小得多(取决于您的语言).

  • [这是一个用JavaScript编写的在线演示](http://jsfiddle.net/rg5qu/). (8认同)
  • 优化:sin(Pi/2)= 1,cos(Pi/2)= 0.因此,您将获得旋转矩阵[[0,-1],[1,0]](逆时针旋转). (3认同)
  • 对于俄罗斯方块使用的简单旋转,不需要所有这种矩阵乘法mumbo jumbo.请参阅此答案以获取简单的方法:http://stackoverflow.com/a/8664879/239916 (2认同)

Dav*_*ray 12

这是我最近在基于jQuery/CSS的俄罗斯方块游戏中做到的.

计算出块的中心(用作枢轴点),即块形的中心.称之为(px,py).

构成块形状的每个砖块将围绕该点旋转.对于每块砖,您可以应用以下计算...

每块砖的宽度和高度为q,砖的当前位置(左上角)为(x1,y1),新砖位置为(x2,y2):

x2 = (y1 + px - py)

y2 = (px + py - x1 - q)
Run Code Online (Sandbox Code Playgroud)

要反转方向:

x2 = (px + py - y1 - q)

y2 = (x1 + py - px)
Run Code Online (Sandbox Code Playgroud)

该计算基于2D仿射矩阵变换.如果您对我的工作感兴趣,请告诉我.


Jon*_*eet 11

就个人而言,我总是只是手工代表旋转 - 只有很少的形状,很容易编码.基本上我有(作为伪代码)

class Shape
{
    Color color;
    ShapeRotation[] rotations;
}

class ShapeRotation
{
    Point[4] points;
}

class Point
{
    int x, y;
}
Run Code Online (Sandbox Code Playgroud)

至少在概念上 - 一个直接形状的多维点阵列也可以做到这一点:)


小智 7

您只能通过对其应用数学运算来旋转矩阵.如果你有一个矩阵,请说:

Mat A = [1,1,1]
        [0,0,1]
        [0,0,0]
Run Code Online (Sandbox Code Playgroud)

要旋转它,将它乘以它的转置,再乘以这个矩阵([I] dentity [H] orizo​​ntaly [M] irrored):

IHM(A) = [0,0,1]
         [0,1,0]
         [1,0,0]
Run Code Online (Sandbox Code Playgroud)

然后你会有:

Mat Rotation = Trn(A)*IHM(A) = [1,0,0]*[0,0,1] = [0,0,1]
                               [1,0,0] [0,1,0] = [0,0,1]
                               [1,1,0] [1,0,0] = [0,1,1]
Run Code Online (Sandbox Code Playgroud)

注意:旋转中心将是矩阵的中心,在这种情况下为(2,2).

  • 为什么这是一张图片? (14认同)

fer*_*rit 7

表示

表示最小矩阵中的每一块,其中 1 代表 tetriminoe 占据的空间,0 代表空白空间。例子:

originalMatrix = 
[0,   0,   1]
[1,   1,   1]
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

旋转公式

clockwise90DegreesRotatedMatrix = reverseTheOrderOfColumns(Transpose(originalMatrix))

anticlockwise90DegreesRotatedMatrix = reverseTheOrderOfRows(Transpose(originalMatrix))
Run Code Online (Sandbox Code Playgroud)

插图

originalMatrix = 
  x    y    z
a[0,   0,   1]
b[1,   1,   1]
Run Code Online (Sandbox Code Playgroud)

transposed = transpose(originalMatrix)
  a   b
x[0,  1]
y[0,  1]
z[1,  1]
Run Code Online (Sandbox Code Playgroud)

counterClockwise90DegreesRotated = reverseTheOrderOfRows(transposed)
  a   b
z[1,  1]
y[0,  1]
x[0,  1]
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

clockwise90DegreesRotated = reverseTheOrderOfColumns(transposed)
  b   a
x[1,  0]
y[1,  0]
z[1,  1]
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明


Age*_*een 6

由于每个形状只有4个可能的方向,为什么不使用形状的状态数组和旋转CW或CCW简单地增加或减少形状状态的索引(包含索引)?我认为这可能比执行旋转计算等更快.

  • 对于俄罗斯方块,这很容易.这是我最喜欢的方式. (6认同)