像素碰撞检测不工作

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 上而不绘制任何内容来生成。即使在做了那个碰撞算法之后似乎也不太好用。

我创建了两个变量 monsterImageDataheroImageData 来保存图像数据,这些变量只加载一次。

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);