像素碰撞检测不工作
Pixel Collision Detection Not Working
这是我的游戏plnkr。
(编辑:另一个 plnkr 有一个静态怪物而不是多个动态怪物)
输入或按钮将重新开始游戏。
谁能告诉我为什么从 here 中提取的碰撞检测算法不起作用?它似乎没有准确地检测到命中(太广泛)。他们网站上的演示效果很好,但我不确定我做错了什么。
最相关的代码段(内部更新函数):
// Are they touching?
if (heroImage.width) {
var heroImageData = ctx.getImageData(heroImage.x, heroImage.y, heroImage.width, heroImage.height);
var monsterImageData;
for (var i = 0; i < monsters.length; i++) {
var monster = monsters[i];
monster.x += monster.directionVector.x;
monster.y += monster.directionVector.y;
monsterImageData = ctx.getImageData(monster.monsterImage.x, monster.monsterImage.y, monster.monsterImage.width, monster.monsterImage.height);
if (isPixelCollision(heroImageData, hero.x, hero.y, monsterImageData, monster.x, monster.y)) {
stop();
}
}
}
您的主图为 71x68 像素,外部有很多透明 space。我猜如果你裁剪它以适合图像,它会减少碰撞之间的 space。
您在游戏的绘图上下文中获取 imageData,因此由于您有背景,所以根本没有透明像素,因此您的像素碰撞检测 returns 始终为真 - > 您只是在做边界框检查,事实上。
该算法的思想是比较两个只需要计算一次的静态imageData(getImageData是一个代价高昂的操作)。
几点建议:
• 在启动游戏之前载入您的图像。
• 重新调整(裁剪)您的图像,它有很多空白,正如@Quantumplate 注意到的那样。
• 在游戏开始之前只计算上下文中精灵的图像数据一次。不要忘记在drawImage + getImageData 之前clearRect() canvas。这是解决您的错误的方法。
• 去掉
if (xDiff < 4 && yDiff < 4) {
以及对应的else。这个 'optimisation' 毫无意义。使用像素检测的要点是要精确。 Redim (crop) 你的图像更重要的是赢得很多时间(但你需要... ?? )
• Rq:像素检测算法写得多么糟糕!!! 1)要四舍五入一个数字,它正在使用! 5 种不同的方法(圆形,<<0,~~,0 |,?:)!!! 2) 当 CPU 缓存首先优先选择 Y 时,它首先在 X 上循环,还有许多其他事情......但现在如果可行......
正如@GameAlchemist 所指出的,您正在从 canvas 背景中为怪物和英雄取 ImageData
,该背景已经用背景图像绘制。因此将始终具有 alpha 值 255(不透明)。
正在检查碰撞函数
if (
( pixels [((pixelX - x ) + (pixelY - y ) * w ) * 4 + 3 /*RGBA, alpha @ 4*/] !== 0/*alpha zero expected*/ ) &&
( pixels2[((pixelX - x2) + (pixelY - y2) * w2) * 4 + 3 /*RGBA, alpha @ 4*/] !== 0/*alpha zero expected*/ )
) {
return true;
}
相反,这两个 ImageData 都应该通过将这些图像绘制到 canvas 上而不绘制任何内容来生成。即使在做了那个碰撞算法之后似乎也不太好用。
我创建了两个变量 monsterImageData
和 heroImageData
来保存图像数据,这些变量只加载一次。
HTML 文件 id=testCanvas
中有一个新的 canvas。用于获取怪物和英雄的图像数据值。
这里是修改后的代码plunker link。
这是另一种(更有效的)像素完美碰撞测试...
准备:对于每张要测试碰撞的图像
- 如前所述,trim 任何超出图像边缘的透明像素,
- 将 canvas 调整为图片大小,(您可以为多张图片重复使用 1 canvas)
- 在canvas、
上绘制图像
- 获取 canvas 的所有像素信息:
context.getImageData
、
- 创建一个仅包含 alpha 信息的数组:如果透明则为 false,否则为 true。
进行像素完美碰撞测试
快速测试图像矩形是否发生碰撞。如果没有,你就完成了。
// r1 & r2 are rect objects {x:,y:,w:.h:}
function rectsColliding(r1,r2){
return(!(
r1.x > r2.x+r2.w ||
r1.x+r1.w < r2.x ||
r1.y > r2.y+r2.h ||
r1.y+r1.h < r2.y
));
}
计算两张图片的相交矩形
// r1 & r2 are rect objects {x:,y:,w:.h:}
function intersectingRect(r1,r2){
var x=Math.max(r1.x,r2.x);
var y=Math.max(r1.y,r2.y);
var xx=Math.min(r1.x+r1.w,r2.x+r2.w);
var yy=Math.min(r1.y+r1.h,r2.y+r2.h);
return({x:x,y:y,w:xx-x,h:yy-y});
}
比较两个 alpha 数组中的相交像素。如果两个阵列在同一位置都有一个不透明的像素,那么就会发生碰撞。请务必通过抵消比较对原点 (x=0,y=0) 进行归一化。
// warning untested code -- might need tweaking
var i=intersectingRect(r1,r2);
var offX=Math.min(r1.x,r2.x);
var offY=Math.min(r1.y,r2.y);
for(var x=i.x-offX; x<=(i.x-offX)+i.w; x++{
for(var y=i.y-offY; y<=(i.y-offY)+i.h; y++{
if(
// x must be valid for both arrays
x<alphaArray1[y].length && x<alphaArray2[y].length &&
// y must be valid for both arrays
y<alphaArray1.length && y<alphaArray2.length &&
// collision is true if both arrays have common non-transparent alpha
alphaArray1[x,y] && alphaArray2[x,y]
){
return(true);
}
}}
return(false);
这是我的游戏plnkr。
(编辑:另一个 plnkr 有一个静态怪物而不是多个动态怪物)
输入或按钮将重新开始游戏。
谁能告诉我为什么从 here 中提取的碰撞检测算法不起作用?它似乎没有准确地检测到命中(太广泛)。他们网站上的演示效果很好,但我不确定我做错了什么。
最相关的代码段(内部更新函数):
// Are they touching?
if (heroImage.width) {
var heroImageData = ctx.getImageData(heroImage.x, heroImage.y, heroImage.width, heroImage.height);
var monsterImageData;
for (var i = 0; i < monsters.length; i++) {
var monster = monsters[i];
monster.x += monster.directionVector.x;
monster.y += monster.directionVector.y;
monsterImageData = ctx.getImageData(monster.monsterImage.x, monster.monsterImage.y, monster.monsterImage.width, monster.monsterImage.height);
if (isPixelCollision(heroImageData, hero.x, hero.y, monsterImageData, monster.x, monster.y)) {
stop();
}
}
}
您的主图为 71x68 像素,外部有很多透明 space。我猜如果你裁剪它以适合图像,它会减少碰撞之间的 space。
您在游戏的绘图上下文中获取 imageData,因此由于您有背景,所以根本没有透明像素,因此您的像素碰撞检测 returns 始终为真 - > 您只是在做边界框检查,事实上。
该算法的思想是比较两个只需要计算一次的静态imageData(getImageData是一个代价高昂的操作)。
几点建议:
• 在启动游戏之前载入您的图像。
• 重新调整(裁剪)您的图像,它有很多空白,正如@Quantumplate 注意到的那样。
• 在游戏开始之前只计算上下文中精灵的图像数据一次。不要忘记在drawImage + getImageData 之前clearRect() canvas。这是解决您的错误的方法。
• 去掉
if (xDiff < 4 && yDiff < 4) {
以及对应的else。这个 'optimisation' 毫无意义。使用像素检测的要点是要精确。 Redim (crop) 你的图像更重要的是赢得很多时间(但你需要... ?? )
• Rq:像素检测算法写得多么糟糕!!! 1)要四舍五入一个数字,它正在使用! 5 种不同的方法(圆形,<<0,~~,0 |,?:)!!! 2) 当 CPU 缓存首先优先选择 Y 时,它首先在 X 上循环,还有许多其他事情......但现在如果可行......
正如@GameAlchemist 所指出的,您正在从 canvas 背景中为怪物和英雄取 ImageData
,该背景已经用背景图像绘制。因此将始终具有 alpha 值 255(不透明)。
正在检查碰撞函数
if (
( pixels [((pixelX - x ) + (pixelY - y ) * w ) * 4 + 3 /*RGBA, alpha @ 4*/] !== 0/*alpha zero expected*/ ) &&
( pixels2[((pixelX - x2) + (pixelY - y2) * w2) * 4 + 3 /*RGBA, alpha @ 4*/] !== 0/*alpha zero expected*/ )
) {
return true;
}
相反,这两个 ImageData 都应该通过将这些图像绘制到 canvas 上而不绘制任何内容来生成。即使在做了那个碰撞算法之后似乎也不太好用。
我创建了两个变量 monsterImageData
和 heroImageData
来保存图像数据,这些变量只加载一次。
HTML 文件 id=testCanvas
中有一个新的 canvas。用于获取怪物和英雄的图像数据值。
这里是修改后的代码plunker link。
这是另一种(更有效的)像素完美碰撞测试...
准备:对于每张要测试碰撞的图像
- 如前所述,trim 任何超出图像边缘的透明像素,
- 将 canvas 调整为图片大小,(您可以为多张图片重复使用 1 canvas)
- 在canvas、 上绘制图像
- 获取 canvas 的所有像素信息:
context.getImageData
、 - 创建一个仅包含 alpha 信息的数组:如果透明则为 false,否则为 true。
进行像素完美碰撞测试
快速测试图像矩形是否发生碰撞。如果没有,你就完成了。
// r1 & r2 are rect objects {x:,y:,w:.h:} function rectsColliding(r1,r2){ return(!( r1.x > r2.x+r2.w || r1.x+r1.w < r2.x || r1.y > r2.y+r2.h || r1.y+r1.h < r2.y )); }
计算两张图片的相交矩形
// r1 & r2 are rect objects {x:,y:,w:.h:} function intersectingRect(r1,r2){ var x=Math.max(r1.x,r2.x); var y=Math.max(r1.y,r2.y); var xx=Math.min(r1.x+r1.w,r2.x+r2.w); var yy=Math.min(r1.y+r1.h,r2.y+r2.h); return({x:x,y:y,w:xx-x,h:yy-y}); }
比较两个 alpha 数组中的相交像素。如果两个阵列在同一位置都有一个不透明的像素,那么就会发生碰撞。请务必通过抵消比较对原点 (x=0,y=0) 进行归一化。
// warning untested code -- might need tweaking var i=intersectingRect(r1,r2); var offX=Math.min(r1.x,r2.x); var offY=Math.min(r1.y,r2.y); for(var x=i.x-offX; x<=(i.x-offX)+i.w; x++{ for(var y=i.y-offY; y<=(i.y-offY)+i.h; y++{ if( // x must be valid for both arrays x<alphaArray1[y].length && x<alphaArray2[y].length && // y must be valid for both arrays y<alphaArray1.length && y<alphaArray2.length && // collision is true if both arrays have common non-transparent alpha alphaArray1[x,y] && alphaArray2[x,y] ){ return(true); } }} return(false);