0

我有一个二维关联数组,其中包含需要在 html5 画布中绘制的块图。

目前完成它大约需要 28 毫秒,这太多了(我每秒运行代码 60 次),我需要以某种方式更有效地运行矩阵。这是我的代码:

for(var x=0; x<Object.keys(matrix).length; x++){ // Run through cols
    col = Object.keys(matrix)[x];
    for(var y=0; y<matrix[col].length; y++){ // Run though rows
        row = Object.keys(matrix[col])[y];
        if(matrix[col][row] != '0'){
            drawRect(col,row,blockSize,blockSize);
            }
        }
    }

我希望它效率低下,并且可以纠正一些问题。如何最有效地循环二维数组的总数?

4

5 回答 5

1

您必须使用双 for 循环来循环整个二维数组。与尝试加速任何存在性能问题的代码一样,第一步是找出花费最多时间的内容,并首先在问题的那个元素上进行优化。

可能的猜测是 drawRect 比循环中的任何问题都花费更多的时间,但是如果您想加快其余代码的速度,您可以执行以下操作:

  1. 缓存Object.keys(matrix),因此不会不断重新计算
  2. 缓存每个循环的停止值,for因此每次循环都不会重新计算它
  3. 缓存列键,因此不会每次通过内部循环重新计算
  4. 缓存矩阵列

该代码如下所示:

var keys = Object.keys(matrix);
for(var x = 0, lenX = keys.length; x < lenX; x++) { // Run through cols
    col = keys[x];
    var colKeys = Object.keys(matrix[col]);
    var matrixCol = matrix[col];
    for(var y = 0, lenY = matrix[col].length; y < lenY; y++) { // Run though rows
        row = colKeys[y];
        matrixCol[row] != '0'){
            drawRect(col,row,blockSize,blockSize);
        }
    }
}
于 2012-12-11T02:37:46.587 回答
1

您应该首先查看花费最长时间的内容。我敢打赌,drawRect 函数是最慢的。不要绘制矩形,而是尝试打印到控制台(或在屏幕上书写)并查看需要多少时间。然后,您可以计算 drawRect 执行 1 次操作需要多少时间。我认为这可能是你的问题,drawRect 需要很长时间才能绘制。

于 2012-12-11T02:38:10.480 回答
0

您可以尝试这样做以减少循环内的评估

var m = Object.keys(matrix), mc, x = 0, xlen = m.length, col, mcol, y, ylen, row;
for(;x < xlen; x++) {
  col = m[x];
  mc = matrix[col];
  for(y = 0, ylen = mc.length; y < ylen; y++) {
    row = mc[y];
    if(mc[row] != '0') {
      drawRect(col, row, blockSize, blockSize);
    }
  }
}

这里的要点是减少对对象键的评估需求。不知道如何减少,因为我不知道这些功能在做什么。

于 2012-12-11T02:45:40.557 回答
0

尝试这个。

for(var x=0, xlimit=Object.keys(matrix).length; x<xlimit; x++){ // Run through cols
    col = Object.keys(matrix)[x];
    for(var y=0, ylimit=matrix[col].length; y<ylimit; y++){ // Run though rows
        row = Object.keys(matrix[col])[y];
        if(matrix[col][row] != '0'){
            drawRect(col,row,blockSize,blockSize);
        }
    }
}
于 2012-12-11T02:36:46.377 回答
0

感谢一些建议缓存大量变量的答案,我将绘图从 30 毫秒缩短到 20 毫秒。

但是今天我设法通过删除 Object.keys() 将其缩短到 1 毫秒,并且因为我知道键所在的范围,所以我更改了 x 和 lenX 以预选范围。与 y 和 lenY 相同。加载时间几乎不会随着矩阵的大小而变化。(我已经测试了一个 100 倍大的矩阵,它只需要 2 毫秒)。

minX = Math.floor(cameraPos.x/blockSize)*blockSize;
minY = Math.floor(cameraPos.y/blockSize)*blockSize;
maxX = minX+WIDTH+blockSize;
maxY = minY+HEIGHT+blockSize;
for(var col = minX; col < maxX; col+=blockSize) { // Run through cols
    for(var row = minY; row < maxY; row+=blockSize) { // Run though rows
        if(typeof matrix[col] !== "undefined" && typeof matrix[col][row] !== "undefined"){
            drawRect(xFix(col),yFix(row),blockSize,blockSize,blockColors[matrix[col][row]]);
        }
    }
}

尽管该功能非常快,但它最终会开始缩放,为了消除所有缩放并且理论上能够使游戏地图大得离谱,我开始在每次相机滚动一定距离时缓存地图(现在它是屏幕宽度的两倍,但可能更多)。由于要缓存的区域使用与此类似的功能进行过滤,因此几乎可以立即完成。

var cached = [];
function cacheMap() {
    if(cached['pos'] === undefined || cameraPos.x < cached['pos']['x']-WIDTH || cameraPos.x > cached['pos']['x']+WIDTH || cameraPos.y < cached['pos']['y']-HEIGHT || cameraPos.y > cached['pos']['y']+HEIGHT) {
        cached['map'] = filterMatrix(map,cameraPos.x-WIDTH-blockSize*5,cameraPos.y-HEIGHT-blockSize*5,WIDTH*3+blockSize*10,HEIGHT*3+blockSize*10);
        cached['pos'] = {'x':cameraPos.x,'y':cameraPos.y};
        console.log('cached map');
        }
    }
于 2012-12-13T06:55:42.787 回答