JS Canvas非常频繁地获取像素值

时间:2015-02-12 02:23:29

标签: javascript canvas webgl pixi.js

我正在创建一个基于Node.js / WebGL / Canvas / PIXI.js的视频游戏。

在这个游戏中,块具有通用尺寸:它们可以是圆形,多边形或一切。因此,我的物理引擎需要知道事物的确切位置,墙壁是什么像素,哪些像素不是。因为我认为PIXI不允许这样做,所以我创建了一个看不见的画布,在那里我放置了地图的所有墙壁图像。然后,我使用函数getImageData创建一个函数" isWall" at(x,y):

function isWall(x, y):
    return canvas.getImageData(x, y, 1, 1).data[3] != 0;

然而,这非常慢(根据Chrome分析,它占用了游戏CPU时间的70%)。此外,由于我介绍了这个功能,我有时会得到错误"糟糕,WebGL崩溃"没有任何额外的建议。

有没有更好的方法来访问像素的值?我想把所有东西都存放在一个静态位数组中(墙壁有一个固定的大小),1对应一个墙,0对应一个非墙。在内存中有一个1000万个单元阵列是否合理?

2 个答案:

答案 0 :(得分:2)

一些想法:

  • 首次检查:对所有对象使用碰撞区域。甚至可以根据形状(即复杂形状)为每一侧定义区域。仅检查交叉区域内的碰撞。
  • 对命中测试位图使用半分辨率(如果您的方案允许,则使用25%)。当物体移动时,我们的大脑无法检测到像素精确的碰撞,因此可以利用它。
  • 对于复杂的形状, pre - 存储整个位图(基于其区域),但将其转换为单个值类型的数组,如Uint8Array with高值和低值(重复使用此值而不是通过上下文获取一个和一个像素)。减去对象的位置,并将结果用作形状区域的增量,然后点击测试"位图"。如果形状旋转,则相应地转换传入的检查点(这里可能存在一个甜点,其中更新位图变得比转换一堆点等更快。您需要测试您的场景。)
  • 对于接近方形的物体做妥协并使用简单的矩形检查
  • 对于圆和椭圆,使用非平方值来检查半径的距离。
  • 在某些情况下,你可以使用你在游戏开始之前计算的碰撞预测以及知道所有物体的位置,方向和速度(计算完整的运动路径,找到这些路径的交叉点,计算)到那些十字路口的时间/距离)。如果你的对象因路径中的其他事件而改变了方向等,那么这当然不会那么好(或者试着看看重新计算是否有益)。

我确定为什么你需要10m存储在内存中,但它可行但是你需要使用类似四叉树的东西并将阵列分开,所以看起来效率很高像素状态。 IMO你只需要存储"比特"对于复杂的形状,您可以通过为每个形状定义多个区域来进一步限制它。对于更简单的形状,只需使用矢量(矩形,半径/距离)。经常进行性能测试以找到合适的平衡点。

在任何情况下 - 这些事情必须针对场景进行手动优化,因此这只是一个普遍的看法。其他因素会影响高速度,旋转,反射等方法,并且很快会变得非常宽泛。希望这会给出一些意见。

答案 1 :(得分:1)

我使用位数组来存储 0 || 1 信息,它运作良好。

信息存储紧凑,获取/设置非常快。

这是我使用的位库:

https://github.com/drslump/Bits-js/blob/master/lib/Bits.js

我没有尝试使用10m位,因此您必须在自己的数据集上进行尝试。

您提出的解决方案非常“平坦”,这意味着每个像素必须具有相应的位。这导致需要大量内存 - 即使信息存储为位。

替代测试数据范围,而不是测试每个像素:

如果墙像素数小于像素总数,您可以尝试将每个墙存储为一系列“运行”。例如,墙运行可能存储在这样的对象中(警告:未经测试的代码!):

// an object containing all horizontal wall runs
var xRuns={}

// an object containing all vertical wall runs
var yRuns={}

// define a wall that runs on y=50 from x=100 to x=185
// and then runs on x=185 from y=50 to y=225
var y=50;
var x=185;

if(!xRuns[y]){ xRuns[y]=[]; }

xRuns[y].push({start:100,end:185});

if(!yRuns[x]){ yRuns[x]=[]; }

yRuns[x].push({start:50,end:225});

然后你可以像这样快速测试墙上的[x,y](警告未经测试的代码!):

function isWall(x,y){

    if(xRuns[y]){
        var a=xRuns[y];
        var i=a.length;
        do while(i--){
            var run=a[i];
            if(x>=run.start && x<=run.end){return(true);}
        }
    }

    if(yRuns[x]){
        var a=yRuns[x];
        var i=a.length;
        do while(i--){
            var run=a[i];
            if(y>=run.start && y<=run.end){return(true);}
        }
    }

    return(false);

}

这应该只需要很少的测试,因为x&amp; y确切地指定需要测试哪个xRuns和yRuns数组。

它可能(或可能不)比测试“平面”模型更快,因为有平面模型的指定元素的开销。你必须使用这两种方法进行性能测试。

壁挂式方法可能需要更少的内存。

希望这会有所帮助......请记住,墙上运行的选择只是我的头脑,可能需要调整; - )