Rotations(轮换)
One problem with actual rotations is that some of them are not going to look all that good, even if the width of the matrix is taken into consideration.
(实际旋转的一个问题是,即使考虑了矩阵的宽度,它们中的某些旋转看起来也不是那么好。)
Let's see what happens with the rotation of the I
shape:(让我们看看旋转I
形会发生什么:)
. X . . . . . . . . X . . . . .
. X . . => X X X X => . . X . => . . . .
. X . . . . . . . . X . X X X X
. X . . . . . . . . X . . . . .
From a gameplay perspective, you would expect the 3 rd and 4 th shapes to be identical to the 1 st and 2 nd ones, respectively.
(从游戏的角度来看,将是必要的第3 次和第 4 次形状是相同的1 次和第 2 次的,分别。)
But it's not what's going to happen with the generic rotation algorithm.(但这不是通用旋转算法会发生的事情。)
You might address the above issue by using a non-square matrix (5x4), but the algorithm is going to get more complicated than you would have initially expected.(您可以通过使用非平方矩阵(5x4)解决上述问题,但是该算法将比您最初预期的更加复杂。)
Actually, I'd be willing to bet that most Tetris implementations do not bother doing the rotation programmatically and simply hardcode all the different possible shapes of the tetrominoes, in a way that makes the rotations look as good and as 'fair' as possible.
(实际上,我愿意打赌,大多数Tetris实现不会费心地通过编程进行旋转,而只是硬编码tetrominos的所有可能的不同形状,以使旋转看起来尽可能的好和“公平”。)
A nice thing about that is that you don't have to worry about their size anymore.(这样做的好处是,您不必再担心它们的大小。)
You can just store them all as 4x4.(您可以将它们全部存储为4x4。)
As we are going to see here, this can be done in a very compact format.
(我们将在这里看到,这可以以非常紧凑的格式完成。)
Encoding tetrominoes as bitmasks(将Tetrominoes编码为位掩码)
Because a tetromino is basically a set of 'big pixels' that can be either on or off , it is quite suitable and efficient to represent it as a bitmask rather than a matrix of integers.
(由于tetromino基本上是一组可以打开或关闭的“大像素”,因此将其表示为位掩码而不是整数矩阵非常合适且有效。)
Let's see how we can encode the two distinct rotations of the S
shape:
(让我们看看如何编码S
形的两个不同的旋转:)
X . . . 1 0 0 0
X X . . = 1 1 0 0 = 1000110001000000 (in binary) = 0x8C40 (in hexadecimal)
. X . . 0 1 0 0
. . . . 0 0 0 0
. X X . 0 1 1 0
X X . . = 1 1 0 0 = 0110110000000000 (in binary) = 0x6C00 (in hexadecimal)
. . . . 0 0 0 0
. . . . 0 0 0 0
The two other rotations are the same for this one.
(这另外两个旋转是相同的。)
So, we can fully define our S
shape with:(因此,我们可以通过以下方式完全定义S
形:)
[ 0x8C40, 0x6C00, 0x8C40, 0x6C00 ]
Doing the same thing for each shape and each rotation, we end up with something like:
(对于每个形状和每个旋转做相同的事情,我们最终得到如下结果:)
var shape = [
[ 0x4640, 0x0E40, 0x4C40, 0x4E00 ], // 'T'
[ 0x8C40, 0x6C00, 0x8C40, 0x6C00 ], // 'S'
[ 0x4C80, 0xC600, 0x4C80, 0xC600 ], // 'Z'
[ 0x4444, 0x0F00, 0x4444, 0x0F00 ], // 'I'
[ 0x44C0, 0x8E00, 0xC880, 0xE200 ], // 'J'
[ 0x88C0, 0xE800, 0xC440, 0x2E00 ], // 'L'
[ 0xCC00, 0xCC00, 0xCC00, 0xCC00 ] // 'O'
];
Drawing them(画他们)
Now, how are we going to draw a tetromino with this new format?
(现在,我们要如何用这种新格式绘制四面体?)
Rather than accessing a value in a matrix with matrix[y][x]
, we're going to test the relevant bit in our bitmask:(而不是使用matrix[y][x]
访问矩阵中的值,我们将测试位掩码中的相关位:)
for (var y = 0; y < 4; y++) {
for (var x = 0; x < 4; x++) {
if (shape[s][r] & (0x8000 >> (y * 4 + x))) {
ctx.fillRect(x * 20, y * 20, 19, 19);
}
}
}
Demo(演示版)
Below is some demonstration code using this method.
(下面是一些使用此方法的演示代码。)
var canvas = document.getElementById('c'); var ctx = canvas.getContext('2d'); canvas.width = 100; canvas.height = 100; var shape = [ [ 0x4640, 0x0E40, 0x4C40, 0x4E00 ], // 'T' [ 0x8C40, 0x6C00, 0x8C40, 0x6C00 ], // 'S' [ 0x4C80, 0xC600, 0x4C80, 0xC600 ], // 'Z' [ 0x4444, 0x0F00, 0x4444, 0x0F00 ], // 'I' [ 0x44C0, 0x8E00, 0xC880, 0xE200 ], // 'J' [ 0x88C0, 0xE800, 0xC440, 0x2E00 ], // 'L' [ 0xCC00, 0xCC00, 0xCC00, 0xCC00 ] // 'O' ]; var curShape = 0, curRotation = 0; draw(curShape, curRotation); function draw(s, r) { ctx.fillStyle = 'white'; ctx.fillRect(0, 0, 100, 100); ctx.fillStyle = 'black'; for (var y = 0; y < 4; y++) { for (var x = 0; x < 4; x++) { if (shape[s][r] & (0x8000 >> (y * 4 + x))) { ctx.fillRect(x * 20, y * 20, 19, 19); } } } } function next() { curShape = (curShape + 1) % 7; draw(curShape, curRotation); } function rotate() { curRotation = (curRotation + 1) % 4; draw(curShape, curRotation); }
<canvas id="c"></canvas> <button onclick="rotate()">Rotate</button> <button onclick="next()">Next shape</button>