JS Canvas 获取像素值非常频繁
JS Canvas get pixel value very frequently
我正在基于 Node.js/WebGL/Canvas/PIXI.js.
创建一个视频游戏
在此游戏中,方块具有通用尺寸:它们可以是圆形、多边形或任何形状。所以,我的物理引擎需要知道事物的确切位置,哪些像素是墙,哪些像素不是。因为我认为 PIXI 不允许这样做,所以我创建了一个不可见的 canvas,我把所有墙上的地图图像都放在那里。然后,我使用函数 getImageData 创建函数 "isWall" at (x, y):
function isWall(x, y):
return canvas.getImageData(x, y, 1, 1).data[3] != 0;
然而,这非常慢(根据 Chrome 分析,它占用了游戏 CPU 时间的 70%。另外,自从我引入了这个功能,我有时会在没有任何额外建议的情况下得到错误"Oops, WebGL crashed"。
是否有更好的方法来访问像素值?我考虑过将所有内容存储在静态位数组中(墙具有固定大小),1 对应墙,0 对应非墙。 1000万个cells的数组在内存中合理吗?
我用位数组来存储0 || 1 条信息,效果很好。
信息存储紧凑,gets/sets非常快。
这是我使用的位库:
https://github.com/drslump/Bits-js/blob/master/lib/Bits.js
我没有尝试过 10m 位,因此您必须在自己的数据集上尝试。
你提出的解决方案非常"flat",也就是说每个像素必须有一个对应的位。这导致需要大量内存——即使信息以位形式存储。
替代测试数据范围而不是测试每个像素:
如果墙像素数相对于像素总数较小,您可以尝试将每面墙存储为一系列 "runs"。例如,一堵墙 运行 可能存储在这样的对象中(警告:未经测试的代码!):
// 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 数组。
它可能(也可能不会)比测试 "flat" 模型更快,因为到达平面模型的指定元素会产生开销。您必须使用这两种方法进行性能测试。
wall-运行 方法可能需要更少的内存。
希望这会有所帮助...请记住 wall-运行 替代方案就在我的脑海中,可能需要调整 ;-)
一些想法:
- 第一次检查:为所有对象使用碰撞区域。甚至可以根据形状(即复杂形状)为每一侧定义区域。仅检查相交区域内的碰撞。
- 对命中测试位图使用一半的分辨率(如果您的场景允许,甚至可以使用 25%)。当物体移动时,我们的大脑无法检测到像素精确的碰撞,因此可以利用这一点。
- 对于复杂的形状,pre-为其存储 whole 位图(基于其区域)但将其转换为一个单值类型的数组,如 Uint8Array,具有高值和低值(重复使用它而不是通过上下文获取一个和一个像素)。减去对象的位置并将结果用作形状区域的增量,然后对 "bitmap" 进行命中测试。如果形状旋转,相应地变换传入的检查点(这里可能有一个最佳点,更新位图比变换一堆点等更快。你需要测试你的场景)。
- 对于接近正方形的物体做折衷,使用简单的矩形检查
- 对于圆和椭圆,使用非平方值来检查半径的距离。
- 在某些情况下,您或许可以使用 碰撞预测,它是您在游戏开始前计算的,并且在知道所有物体的位置、方向和速度(计算完整的运动路径、找到交叉点对于这些路径,计算 time/distance 到那些交叉点)。如果您的对象由于路径中的其他事件而改变方向等,这当然不会很好地工作(或者尝试看看重新计算是否有益)。
我确定为什么您需要在内存中存储 10m,虽然这是可行的 - 但是您需要使用四叉树之类的东西并将数组拆分,以便查找像素状态变得高效. IMO 你只需要为复杂的形状存储 "bits",你可以通过为每个形状定义多个区域来进一步限制它。对于更简单的形状,只需使用矢量(矩形,radius/distance)。经常进行性能测试以找到合适的平衡点。
无论如何 - 这类事情必须针对特定场景进行手动优化,因此这只是一般情况。其他因素会影响方法,例如高速、旋转、反射等,它会很快变得非常广泛。希望这能提供一些意见。
我正在基于 Node.js/WebGL/Canvas/PIXI.js.
创建一个视频游戏在此游戏中,方块具有通用尺寸:它们可以是圆形、多边形或任何形状。所以,我的物理引擎需要知道事物的确切位置,哪些像素是墙,哪些像素不是。因为我认为 PIXI 不允许这样做,所以我创建了一个不可见的 canvas,我把所有墙上的地图图像都放在那里。然后,我使用函数 getImageData 创建函数 "isWall" at (x, y):
function isWall(x, y):
return canvas.getImageData(x, y, 1, 1).data[3] != 0;
然而,这非常慢(根据 Chrome 分析,它占用了游戏 CPU 时间的 70%。另外,自从我引入了这个功能,我有时会在没有任何额外建议的情况下得到错误"Oops, WebGL crashed"。
是否有更好的方法来访问像素值?我考虑过将所有内容存储在静态位数组中(墙具有固定大小),1 对应墙,0 对应非墙。 1000万个cells的数组在内存中合理吗?
我用位数组来存储0 || 1 条信息,效果很好。
信息存储紧凑,gets/sets非常快。
这是我使用的位库:
https://github.com/drslump/Bits-js/blob/master/lib/Bits.js
我没有尝试过 10m 位,因此您必须在自己的数据集上尝试。
你提出的解决方案非常"flat",也就是说每个像素必须有一个对应的位。这导致需要大量内存——即使信息以位形式存储。
替代测试数据范围而不是测试每个像素:
如果墙像素数相对于像素总数较小,您可以尝试将每面墙存储为一系列 "runs"。例如,一堵墙 运行 可能存储在这样的对象中(警告:未经测试的代码!):
// 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 数组。
它可能(也可能不会)比测试 "flat" 模型更快,因为到达平面模型的指定元素会产生开销。您必须使用这两种方法进行性能测试。
wall-运行 方法可能需要更少的内存。
希望这会有所帮助...请记住 wall-运行 替代方案就在我的脑海中,可能需要调整 ;-)
一些想法:
- 第一次检查:为所有对象使用碰撞区域。甚至可以根据形状(即复杂形状)为每一侧定义区域。仅检查相交区域内的碰撞。
- 对命中测试位图使用一半的分辨率(如果您的场景允许,甚至可以使用 25%)。当物体移动时,我们的大脑无法检测到像素精确的碰撞,因此可以利用这一点。
- 对于复杂的形状,pre-为其存储 whole 位图(基于其区域)但将其转换为一个单值类型的数组,如 Uint8Array,具有高值和低值(重复使用它而不是通过上下文获取一个和一个像素)。减去对象的位置并将结果用作形状区域的增量,然后对 "bitmap" 进行命中测试。如果形状旋转,相应地变换传入的检查点(这里可能有一个最佳点,更新位图比变换一堆点等更快。你需要测试你的场景)。
- 对于接近正方形的物体做折衷,使用简单的矩形检查
- 对于圆和椭圆,使用非平方值来检查半径的距离。
- 在某些情况下,您或许可以使用 碰撞预测,它是您在游戏开始前计算的,并且在知道所有物体的位置、方向和速度(计算完整的运动路径、找到交叉点对于这些路径,计算 time/distance 到那些交叉点)。如果您的对象由于路径中的其他事件而改变方向等,这当然不会很好地工作(或者尝试看看重新计算是否有益)。
我确定为什么您需要在内存中存储 10m,虽然这是可行的 - 但是您需要使用四叉树之类的东西并将数组拆分,以便查找像素状态变得高效. IMO 你只需要为复杂的形状存储 "bits",你可以通过为每个形状定义多个区域来进一步限制它。对于更简单的形状,只需使用矢量(矩形,radius/distance)。经常进行性能测试以找到合适的平衡点。
无论如何 - 这类事情必须针对特定场景进行手动优化,因此这只是一般情况。其他因素会影响方法,例如高速、旋转、反射等,它会很快变得非常广泛。希望这能提供一些意见。