我试图在JS中使用矩阵而不是精灵来编写俄罗斯方块。 基本上要更好地可视化2D阵列。
我通过转置矩阵数据然后反转行来旋转块。 但是因为块的宽度和高度并没有完全填满这个4x4矩阵 旋转导致块移动,而不是旋转到位。
我无法看到它,我已经花了两天多的时间试图让这个简单的游戏像俄罗斯方块一样工作,从头开始重启几次...... 我需要帮助,我真的希望能够编写游戏,我唯一能做的就是tic tac toe。我花了更多的时间,而不是我应该做的。
这里是完整的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);
&#13;
<canvas id="c"></canvas>
&#13;
请忽略我的代码中的小怪癖,我自己学习编程。 在此先感谢:)
答案 0 :(得分:3)
我认为你的问题是你总是假设你的作品是4个瓷砖宽。您可能希望将矩阵收缩到最小的仍为正方形的空间。对于你的Z / S块,它将是3x3。然后你的轮换的中心会正常运作。
你现在的问题是轮换工作正常,但你的砖的中心是在单元格(2,2)而不是(1,1)(假设基数为0)。 C
是围绕其应用轮换的参考框架。
[x][ ][ ][ ][ ] [ ][ ][X][X][ ]
[X][X][ ][ ][ ] [ ][X][X][ ][ ]
[ ][X][C][ ][ ] => [ ][ ][C][ ][ ]
[ ][ ][ ][ ][ ] [ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ] [ ][ ][ ][ ][ ]
如果你可以收缩你的形状,你可以应用你的旋转并实现以下目的:
[x][ ][ ] [ ][X][X]
[X][C][ ] => [X][C][ ]
[ ][X][ ] [ ][ ][ ]
答案 1 :(得分:3)
实际旋转的一个问题是,即使考虑矩阵的宽度,其中一些也不会看起来那么好。让我们看一下“我”的轮换会发生什么。形状:
. X . . . . . . . . X . . . . .
. X . . => X X X X => . . X . => . . . .
. X . . . . . . . . X . X X X X
. X . . . . . . . . X . . . . .
从游戏玩法的角度来看,你会期望第3和第4个形状与第1和第2个形状相同。但它并不是通用旋转算法会发生什么。您可以使用非方阵(5x4)解决上述问题,但算法会比您最初预期的要复杂得多。
实际上,我愿意打赌,大多数俄罗斯方块的实现都不打算以编程方式进行旋转,只需对所有不同形状的四联骨牌进行硬编码,这样可以使旋转看起来和旋转一样好。 #39;一般&#39;尽可能。一个好消息是你不必再担心它们的大小了。您可以将它们全部存储为4x4。
正如我们将在这里看到的,这可以以非常紧凑的格式完成。
您的努力值得称赞,我的观点是不要阻止您使用当前的解决方案。恰恰相反。我想提出另一种方法,您可能希望稍后进行实验。
因为tetromino基本上是一组大像素&#39;这可以是&#39; on&#39;或者&#39; off&#39;,将它表示为位掩码而不是整数矩阵是非常合适和有效的。
让我们看看我们如何编码&#39; S&#39;形状:
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
另外两个轮换对于这个轮换是相同的。因此,我们可以完全定义我们的&#39; S&#39;形状:
[ 0x8C40, 0x6C00, 0x8C40, 0x6C00 ]
对每个形状和每个旋转做同样的事情,我们最终得到类似的东西:
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'
];
现在,我们如何使用这种新格式绘制tetromino?我们不是使用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);
}
}
}
以下是使用此方法的一些演示代码。
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);
}
&#13;
<canvas id="c"></canvas>
<button onclick="rotate()">Rotate</button>
<button onclick="next()">Next shape</button>
&#13;