我正在尝试使用矩阵而不是 Sprite 在 JS 中编写俄罗斯方 block 。 基本上是为了更好地可视化二维数组。
我通过转置其矩阵数据然后反转行来旋转 block 。 但是因为 block 的宽度和高度没有完全填满这个 4x4 矩阵 旋转导致 block 移动,而不是原地旋转。
我看不到它,我已经花了两天多的时间试图让像俄罗斯方 block 这样的简单游戏正常工作,从头开始重新启动几次.. 我需要帮助,我真的很想能够编写游戏,而我唯一能做的就是井字游戏。我花了比我应该花的更多的时间。
这是完整的js代码。单击 Canvas 可旋转作品。
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>
请忽略我代码中的小怪癖,我是自学编程的。 提前致谢:)
最佳答案
实际旋转的一个问题是,即使考虑到矩阵的宽度,其中一些旋转看起来也不会那么好。让我们看看 I 形状的旋转会发生什么:
. X . . . . . . . . X . . . . .
. X . . => X X X X => . . X . => . . . .
. X . . . . . . . . X . X X X X
. X . . . . . . . . X . . . . .
从游戏玩法的 Angular 来看,您会期望第 3rd 和第 4th 形状与第 1st 和 2 相同>nd 分别是。但这不是通用旋转算法会发生的事情。您可能会使用非方阵 (5x4) 来解决上述问题,但算法会变得比您最初预期的更复杂。
实际上,我敢打赌,大多数俄罗斯方 block 实现都不会费心以编程方式进行旋转,而是简单地对四联骨牌的所有不同可能形状进行硬编码,从而使旋转看起来既好又“公平”尽可能。这样做的好处是您不必再担心它们的大小。您可以将它们全部存储为 4x4。
正如我们将在此处看到的,这可以以非常紧凑的格式完成。
因为四联骨牌基本上是一组可以开启或关闭的“大像素”,将其表示为位掩码非常合适且有效而不是整数矩阵。
让我们看看如何对 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
其他两个旋转与此相同。因此,我们可以完全定义我们的 S 形状:
[ 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'
];
现在,我们将如何使用这种新格式绘制四联骨牌?我们将测试位掩码中的相关位,而不是使用 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);
}<canvas id="c"></canvas>
<button onclick="rotate()">Rotate</button>
<button onclick="next()">Next shape</button>
关于javascript - 俄罗斯方 block 二维数组逻辑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38594574/
我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代
我的代码目前看起来像这样numbers=[1,2,3,4,5]defpop_threepop=[]3.times{pop有没有办法在一行中完成pop_three方法中的内容?我基本上想做类似numbers.slice(0,3)的事情,但要删除切片中的数组项。嗯...嗯,我想我刚刚意识到我可以试试slice! 最佳答案 是numbers.pop(3)或者numbers.shift(3)如果你想要另一边。 关于ruby-多次弹出/移动ruby数组,我们在StackOverflow上找到一
我需要读入一个包含数字列表的文件。此代码读取文件并将其放入二维数组中。现在我需要获取数组中所有数字的平均值,但我需要将数组的内容更改为int。有什么想法可以将to_i方法放在哪里吗?ClassTerraindefinitializefile_name@input=IO.readlines(file_name)#readinfile@size=@input[0].to_i@land=[@size]x=1whilex 最佳答案 只需将数组映射为整数:@land边注如果你想得到一条线的平均值,你可以这样做:values=@input[x]
我正在使用puppet为ruby程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这
我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere
这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife
我在理解Enumerator.new方法的工作原理时遇到了一些困难。假设文档中的示例:fib=Enumerator.newdo|y|a=b=1loopdoy[1,1,2,3,5,8,13,21,34,55]循环中断条件在哪里,它如何知道循环应该迭代多少次(因为它没有任何明确的中断条件并且看起来像无限循环)? 最佳答案 Enumerator使用Fibers在内部。您的示例等效于:require'fiber'fiber=Fiber.newdoa=b=1loopdoFiber.yieldaa,b=b,a+bendend10.times.m
我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat
我正在尝试在Ruby中制作一个cli应用程序,它接受一个给定的数组,然后将其显示为一个列表,我可以使用箭头键浏览它。我觉得我已经在Ruby中看到一个库已经这样做了,但我记不起它的名字了。我正在尝试对soundcloud2000中的代码进行逆向工程做类似的事情,但他的代码与SoundcloudAPI的使用紧密耦合。我知道cursesgem,我正在考虑更抽象的东西。广告有没有人见过可以做到这一点的库或一些概念证明的Ruby代码可以做到这一点? 最佳答案 我不知道这是否是您正在寻找的,但也许您可以使用我的想法。由于我没有关于您要完成的工作
我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>