俄罗斯方块2D阵列逻辑

onl*_*349 5 javascript arrays matrix multidimensional-array

我试图用矩阵而不是精灵在JS中编写俄罗斯方块。基本上是要更好地可视化二维数组。

我通过转置块的矩阵数据然后反转行来旋转块。但是,由于块的宽度和高度不能完全填充此4x4矩阵,因此旋转会导致块移动,而不是原地旋转。

我看不到它,我已经花了超过两天的时间尝试让tetris这样简单的游戏正常工作,从头开始重新启动了几次。 ,而我唯一要做的就是井字游戏。我花了比我更多的时间。

这是完整的js代码。单击画布将旋转作品。

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);
Run Code Online (Sandbox Code Playgroud)
<canvas id="c"></canvas>
Run Code Online (Sandbox Code Playgroud)

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

Arn*_*uld 5

轮换

实际旋转的一个问题是,即使考虑了矩阵的宽度,它们中的一些看起来也不会那么好。让我们看看I形状的旋转会发生什么:

. X . .        . . . .        . . X .        . . . .
. X . .   =>   X X X X   =>   . . X .   =>   . . . .
. X . .        . . . .        . . X .        X X X X
. X . .        . . . .        . . X .        . . . .
Run Code Online (Sandbox Code Playgroud)

从游戏的角度来看,您所期待的3和4形状是相同的1和2的,分别。但这不是通用旋转算法会发生的事情。您可以通过使用非方阵 (5x4) 来解决上述问题,但该算法将变得比您最初预期的更复杂。

实际上,我敢打赌,大多数俄罗斯方块实现不会费心以编程方式进行旋转,而是简单地硬编码所有可能的四联体形状,以一种使旋转看起来尽可能好和“公平”的方式。这样做的好处是您不必再担心它们的大小。您可以将它们全部存储为 4x4。

正如我们将在这里看到的,这可以以非常紧凑的格式完成。

将 tetromino 编码为位掩码

因为 tetromino 基本上是一组可以打开关闭的“大像素”,所以将其表示为位掩码而不是整数矩阵是非常合适和有效的。

让我们看看如何对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
Run Code Online (Sandbox Code Playgroud)

其他两个旋转与此相同。所以,我们可以完全定义我们的S形状:

[ 0x8C40, 0x6C00, 0x8C40, 0x6C00 ]
Run Code Online (Sandbox Code Playgroud)

对每个形状和每次旋转都做同样的事情,我们最终得到如下结果:

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'
];
Run Code Online (Sandbox Code Playgroud)

绘制它们

现在,我们将如何用这种新格式绘制一个四元组?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);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

演示

下面是一些使用这种方法的演示代码。

. X . .        . . . .        . . X .        . . . .
. X . .   =>   X X X X   =>   . . X .   =>   . . . .
. X . .        . . . .        . . X .        X X X X
. X . .        . . . .        . . X .        . . . .
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)

  • 首先,为什么按钮标签的 onclick 参数对我不起作用?我总是不得不在我的 js 代码中使用事件侦听器。其次,你能告诉我这段“(0x8000 &gt;&gt; (y * 4 + x))”的代码是做什么的?我什至没有在 Javascript 中看到那个运算符。谢谢,我以前没有听说过位掩码,学习使用它们可能会非常有用。 (2认同)
  • @onlyme0349 运算符是“按位右移运算符”。[这里](http://code.tutsplus.com/articles/understanding-bitwise-operators--active-11301) 是位运算符的一个很好的介绍。 (2认同)
  • @onlyme0349 基本上,我们从位掩码的最高有效位(0x8000 = 第 16 位)开始,根据 x 和 y 将其向右移动 0 到 15 个位置(`&gt;&gt;`),然后进行按位与( `&amp;`) 与 tetromino 位掩码,以确定是否设置了此单元格。 (2认同)
  • `0x` 是用于十六进制表示法的前缀。您可以在 [此处](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Numbers_and_dates) 了解有关数字文字的更多信息。 (2认同)

zer*_*298 3

我认为你的问题是你总是假设你的作品有 4 个方块宽。您可能希望将矩阵收缩到仍然是正方形的最小空间。对于您的 Z/S 块,它将是 3x3。那么你的旋转中心就会正确运作。

您现在的问题是旋转工作正常,但砖块的中心位于单元格 (2, 2) 而不是 (1, 1)(假设基数为 0)。C是旋转所围绕的参考系。

[x][ ][ ][ ][ ]      [ ][ ][X][X][ ]
[X][X][ ][ ][ ]      [ ][X][X][ ][ ]
[ ][X][C][ ][ ]  =>  [ ][ ][C][ ][ ]
[ ][ ][ ][ ][ ]      [ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ]      [ ][ ][ ][ ][ ]
Run Code Online (Sandbox Code Playgroud)

如果您可以收缩包裹您的形状,则可以应用旋转并实现以下目标:

[x][ ][ ]      [ ][X][X]
[X][C][ ]  =>  [X][C][ ]
[ ][X][ ]      [ ][ ][ ]
Run Code Online (Sandbox Code Playgroud)