用于表示和旋转俄罗斯方块游戏的最佳算法(和解释)是什么?我总觉得这件作品的旋转和表现方案令人困惑。
大多数俄罗斯方块游戏似乎都在每次轮换时使用一个天真的“重制阵列块”:
http://www.codeplex.com/Project/ProjectDirectory.aspx?ProjectSearchText=tetris
然而,有些人使用预先建立的编码数字和位移代表每一部分:
http://www.codeplex.com/wintris
有没有一种方法可以使用数学来做到这一点(不确定哪种方法适用于基于单元的电路板)?
答案 0 :(得分:31)
形状有限,所以我会使用固定的表格而不进行计算。这节省了时间。
但是有旋转算法。
选择一个中心点并旋转pi / 2。
如果一块的块从(1,2)开始,它顺时针移动到(2,-1)和(-1,-2)和(-1,2)。 对每个块应用此项并旋转。
每个x是前一个y,每个y是前一个x。这给出了以下矩阵:
[ 0 1 ]
[ -1 0 ]
对于逆时针旋转,请使用:
[ 0 -1 ]
[ 1 0 ]
答案 1 :(得分:24)
当我试图弄清楚旋转对我的俄罗斯方块游戏有多大作用时,这是我在堆栈溢出时发现的第一个问题。虽然这个问题很老,但我认为我的输入会帮助其他人试图通过算法来解决这个问题。首先,我不同意硬编码每个部分和旋转将更容易。 Gamecat的答案是正确的,但我想详细说明。以下是我用来解决Java中的旋转问题的步骤。
对于每个形状,确定其原点。我使用this page图表上的点来指定我的原点。请记住,根据您的实施情况,您可能必须在每次用户移动作品时修改原点。
旋转假设原点位于点(0,0),因此您必须先转换每个块才能旋转它。例如,假设您的原点当前位于点(4,5)。这意味着在旋转形状之前,每个块必须在x坐标中转换为-4,在y坐标中转换为-5,以相对于(0,0)。
在Java中,典型的坐标平面从左上角的点(0,0)开始,然后向右和向下增加。为了在我的实现中补偿这一点,我在旋转前将每个点乘以-1。
以下是我用来计算逆时针旋转后的新x和y坐标的公式。有关这方面的更多信息,我会查看Rotation Matrix上的维基百科页面。 x'和y'是新坐标:
x'= x * cos(PI / 2) - y * sin(PI / 2)和y'= x * sin(PI / 2)+ y * cos(PI / 2)
最后一步,我刚按相反的顺序完成了第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());
}
这种方法是将形状向左旋转所需的全部内容,结果比定义每个形状的每个旋转要小得多(取决于您的语言)。
答案 2 :(得分:12)
这是我最近在基于jQuery / CSS的俄罗斯方块游戏中做到的。
计算出块的中心(用作枢轴点),即块形的中心。 称之为(px,py)。
构成块形状的每个砖块将围绕该点旋转。 对于每个砖块,您可以应用以下计算...
每块砖的宽度和高度为q时,砖的当前位置(左上角)为(x1,y1),新砖位置为(x2,y2):
x2 = (y1 + px - py)
y2 = (px + py - x1 - q)
向相反方向旋转:
x2 = (px + py - y1 - q)
y2 = (x1 + py - px)
该计算基于2D仿射矩阵变换。 如果您对我的工作感兴趣,请告诉我。
答案 3 :(得分:11)
就我个人而言,我总是只是手工代表旋转 - 只有很少的形状,很容易编码。基本上我有(作为伪代码)
class Shape
{
Color color;
ShapeRotation[] rotations;
}
class ShapeRotation
{
Point[4] points;
}
class Point
{
int x, y;
}
至少从概念上讲 - 直接形状的多维点阵也可以解决这个问题:)
答案 4 :(得分:7)
您只能通过对其应用数学运算来旋转矩阵。如果你有一个矩阵,请说:
Mat A = [1,1,1]
[0,0,1]
[0,0,0]
要旋转它,将它乘以它的转置,再乘以这个矩阵([I] dentity [H] orizontaly [M] irrored):
IHM(A) = [0,0,1]
[0,1,0]
[1,0,0]
然后你会:
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]
注意:旋转中心将是矩阵的中心,在本例中为(2,2)。
答案 5 :(得分:6)
由于每个形状只有4个可能的方向,为什么不使用状态数组来旋转CW或CCW只是增加或减少形状状态的索引(包含索引的环绕)?我认为这可能比执行旋转计算更快。
答案 6 :(得分:3)
我从矩阵旋转here派生了一个旋转算法。总结一下:如果你有一个构成块的所有单元格的坐标列表,例如[(0,1),(1,1),(2,1),(3,1)]或[(1,0),(0,1),(1,1),(2,1) ]:
0123 012
0.... 0.#.
1#### or 1###
2.... 2...
3....
您可以使用
计算新坐标x_new = y_old
y_new = 1 - (x_old - (me - 2))
顺时针旋转和
x_new = 1 - (y_old - (me - 2))
y_new = x_old
逆时针旋转。 me
是块的最大范围,即I块的4
,O块的2
和所有其他块的3
。
答案 7 :(得分:3)
<强>表示强>
将每个部分表示在最小矩阵中,其中1表示tetriminoe占据的空间,0表示空的空间。例如:
originalMatrix =
[0, 0, 1]
[1, 1, 1]
轮播公式
clockwise90DegreesRotatedMatrix = reverseTheOrderOfColumns(Transpose(originalMatrix))
anticlockwise90DegreesRotatedMatrix = reverseTheOrderOfRows(Transpose(originalMatrix))
<强>插图强>
originalMatrix =
x y z
a[0, 0, 1]
b[1, 1, 1]
transposed = transpose(originalMatrix)
a b
x[0, 1]
y[0, 1]
z[1, 1]
counterClockwise90DegreesRotated = reverseTheOrderOfRows(transposed)
a b
z[1, 1]
y[0, 1]
x[0, 1]
clockwise90DegreesRotated = reverseTheOrderOfColumns(transposed)
b a
x[1, 0]
y[1, 0]
z[1, 1]
答案 8 :(得分:2)
如果你在python中这样做,基于单元格而不是坐标对,旋转嵌套列表非常简单。
rotate = lambda tetrad: zip(*tetrad[::-1])
# S Tetrad
tetrad = rotate([[0,0,0,0], [0,0,0,0], [0,1,1,0], [1,1,0,0]])
答案 9 :(得分:2)
如果我们假设tetromino的中心方块的坐标(x0,y0)保持不变,则Java中其他3个方格的旋转将如下所示:
private void rotateClockwise()
{
if(rotatable > 0) //We don't rotate tetromino O. It doesn't have central square.
{
int i = y1 - y0;
y1 = (y0 + x1) - x0;
x1 = x0 - i;
i = y2 - y0;
y2 = (y0 + x2) - x0;
x2 = x0 - i;
i = y3 - y0;
y3 = (y0 + x3) - x0;
x3 = x0 - i;
}
}
private void rotateCounterClockwise()
{
if(rotatable > 0)
{
int i = y1 - y0;
y1 = (y0 - x1) + x0;
x1 = x0 + i;
i = y2 - y0;
y2 = (y0 - x2) + x0;
x2 = x0 + i;
i = y3 - y0;
y3 = (y0 - x3) + x0;
x3 = x0 + i;
}
}
答案 10 :(得分:1)
适用于3x3尺寸的俄罗斯方块 翻转你的作品的x和y 然后交换外部列 这就是我想出来的时间
答案 11 :(得分:0)
我使用了一个形状位置,并为所有形状中的四个点设置了四个坐标。由于它位于2D空间中,因此您可以轻松地将2D旋转矩阵应用于这些点。
这些点是div,因此他们的css类从关闭转为开启。 (这是在清除css类之后的最后一个位置。)
答案 12 :(得分:0)
如果数组大小为3 * 3,则以最简单的方式旋转它(例如逆时针方向):
oldShapeMap[3][3] = {{1,1,0},
{0,1,0},
{0,1,1}};
bool newShapeMap[3][3] = {0};
int gridSize = 3;
for(int i=0;i<gridSize;i++)
for(int j=0;j<gridSize;j++)
newShapeMap[i][j] = oldShapeMap[j][(gridSize-1) - i];
/*newShapeMap now contain:
{{0,0,1},
{1,1,1},
{1,0,0}};
*/
答案 13 :(得分:0)
至少在Ruby中,你实际上可以使用矩阵。将您的棋子形状表示为嵌套的数组数组,如[[0,1],[0,2],[0,3]]
require 'matrix'
shape = shape.map{|arr|(Matrix[arr] * Matrix[[0,-1],[1,0]]).to_a.flatten}
然而,我同意对形状进行硬编码是可行的,因为每个= 28行有7种形状和4种状态,它永远不会超过这种状态。
有关详细信息,请参阅我的博客文章 http://pivotallabs.com/the-simplest-thing-that-could-possibly-work-in-tetris/以及https://github.com/andrewfader/Tetronimo
上完全正常的实施(包含小错误)答案 14 :(得分:0)
的Python:
pieces = [
[(0,0),(0,1),(0,2),(0,3)],
[(0,0),(0,1),(1,0),(1,1)],
[(1,0),(0,1),(1,1),(1,2)],
[(0,0),(0,1),(1,0),(2,0)],
[(0,0),(0,1),(1,1),(2,1)],
[(0,1),(1,0),(1,1),(2,0)]
]
def get_piece_dimensions(piece):
max_r = max_c = 0
for point in piece:
max_r = max(max_r, point[0])
max_c = max(max_c, point[1])
return max_r, max_c
def rotate_piece(piece):
max_r, max_c = get_piece_dimensions(piece)
new_piece = []
for r in range(max_r+1):
for c in range(max_c+1):
if (r,c) in piece:
new_piece.append((c, max_r-r))
return new_piece
答案 15 :(得分:0)
在 Java 中:
private static char[][] rotateMatrix(char[][] m) {
final int h = m.length;
final int w = m[0].length;
final char[][] t = new char[h][w];
for(int y = 0; y < h; y++) {
for(int x = 0; x < w; x++) {
t[w - x - 1][y] = m[y][x];
}
}
return t;
}
在 Java 中作为单页应用程序的简单俄罗斯方块实现: https://github.com/vadimv/rsp-tetris