Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
429 views
in Technique[技术] by (71.8m points)

javascript - 俄罗斯方块2D阵列逻辑(Tetris 2d array logic)

I'm trying to write tetris in JS with matrixes instead of sprites.

(我试图用矩阵而不是精灵在JS中编写俄罗斯方块。)

Basically to be better at visualising 2d arrays.

(基本上是要更好地可视化二维数组。)

I rotate a block by transposing its matrix data and then reversing the rows.

(我通过转置块的矩阵数据然后反转行来旋转块。)

But because the block's width and height doesn't completely fill this 4x4 matrix the rotating results in the block moving, instead of rotating in place.

(但是,由于块的宽度和高度不能完全填充此4x4矩阵,因此旋转会导致块移动,而不是原地旋转。)

I can't see it, i've already spent more than two days with trying to get such a simple game as tetris working, restarting from scratch a couple of times.. I need help, i really want to be able to program games, and the only thing i got working was tic tac toe.

(我看不到,我已经花了超过两天的时间尝试让tetris这样简单的游戏正常工作,从头开始重新启动了几次。.我需要帮助,我真的很想能够编写游戏程序,而我唯一要做的就是井字游戏。)

Which i spent more time on than i should.

(我花了比我更多的时间。)

Here's the full js code.

(这是完整的js代码。)

Clicking the canvas rotates the piece.

(单击画布将旋转作品。)

 var canvas = document.getElementById('c'); var ctx = canvas.getContext('2d'); canvas.width = 400; canvas.height = 600; // game object var G = {}; var current = 0; var x = 0; var y = 0; //GRID G.grid = []; G.gridColumns = 10; G.gridRows = 15; for (var i = 0; i < G.gridColumns; i++) { G.grid[i] = []; for (var j = 0; j < G.gridRows; j++) { G.grid[i][j] = 0; } } // Array with all different blocks G.blocks = []; //block constructor function block() {}; G.blocks[0] = new block(); G.blocks[0].matrix = [ [1, 0, 0, 0], [1, 1, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0] ]; G.blocks[0].width = 2; G.blocks[0].height = 3; function transpose(m) { // dont understand this completely, but works because j<i for (var i = 0; i < m.matrix.length; i++) { for (var j = 0; j < i; j++) { var temp = m.matrix[i][j]; m.matrix[i][j] = m.matrix[j][i]; m.matrix[j][i] = temp; } } } function reverseRows(m) { for (var i = 0; i < m.matrix.length; i++) { m.matrix[i].reverse(); } } function rotate(m) { transpose(m); reverseRows(m); } function add(m1, m2) { for (var i = 0; i < m1.matrix.length; i++) { for (var j = 0; j < m1.matrix[i].length; j++) { m2[i + x][j + y] = m1.matrix[i][j]; } } } function draw(matrix) { for (var i = 0; i < matrix.length; i++) { for (var j = 0; j < matrix[i].length; j++) { if (matrix[i][j] === 1) { ctx.fillRect(j * 20, i * 20, 19, 19); } } } ctx.strokeRect(0, 0, G.gridColumns * 20, G.gridRows * 20); } window.addEventListener("click", function(e) { rotate(G.blocks[current]); }); function tick() { ctx.clearRect(0, 0, canvas.width, canvas.height); add(G.blocks[current], G.grid); draw(G.grid); } setInterval(tick, 1000 / 30); 
 <canvas id="c"></canvas> 

Please ignore the little quirks in my code, i learned programming by myself.

(请忽略我代码中的一些小问题,因为我是自己学习编程的。)

Thanks in advance :)

(提前致谢 :))

  ask by onlyme0349 translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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> 


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...