JS Canvas经常获取像素值

JS Canvas get pixel value very frequently

本文关键字:获取 像素 Canvas 常获取 JS      更新时间:2023-10-03

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

在这个游戏中,区块有一个通用的大小:它们可以是圆形、多边形或任何东西。所以,我的物理引擎需要知道东西到底在哪里,什么像素是墙,什么像素不是。由于我认为PIXI不允许这样做,我创建了一个不可见的画布,在那里我放置了地图的所有墙壁图像。然后,我使用函数getImageData在(x,y):处创建一个函数"isWall"

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

然而,这非常慢(根据Chrome评测,它占用了游戏CPU时间的70%)。此外,自从我引入这个功能以来,我有时会在没有任何额外建议的情况下收到错误"哎呀,WebGL崩溃了"。

有没有更好的方法来访问像素的值?我考虑将所有内容存储在一个静态位阵列中(墙有固定的大小),其中1对应于墙,0对应于非墙。在内存中有一个1000万个单元阵列合理吗?

一些想法:

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

我很确定为什么你需要在内存中存储10米,尽管这是可行的,但你需要使用类似四叉树的东西来拆分阵列,这样查找像素状态就变得高效了。IMO您只需要为复杂的形状存储"位",您可以通过为每个形状定义多个区域来进一步限制它。对于更简单的形状,只需使用矢量(矩形、半径/距离)。经常进行性能测试以找到正确的平衡。

在任何情况下,这类事情都必须针对特定的场景进行手动优化,因此这只是一个普遍的看法。其他因素会影响方法,如高速、旋转、反射等,它将很快变得非常广泛。希望这能提供一些意见。

我使用位数组来存储0|1信息,它运行得很好。

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

这是我使用的位库:

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

我还没有尝试过1000万比特,所以你必须在自己的数据集上尝试。

您提出的解决方案是非常"平坦"的,这意味着每个像素都必须有相应的位。这导致需要大量的内存——即使信息是以位的形式存储的。

替代测试每个像素的测试数据范围:

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

// 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&y精确地指定需要测试xRuns和yRuns的哪个数组。

它可能(也可能)比测试"平面"模型更快,因为到达平面模型的指定元素会有开销。您必须同时使用这两种方法进行测试。

墙跑方法可能需要更少的内存。

希望这能帮助。。。请记住,墙跑的替代方案就在我的脑海中,可能需要调整;-)