像素碰撞检测不起作用

时间:2015-12-26 03:10:59

标签: javascript html5 canvas collision-detection game-physics

这是我的游戏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();
        }
    }
}

4 个答案:

答案 0 :(得分:2)

你的英雄形象是71x68像素,外面有很多透明空间。我猜你是否裁剪它只是为了适应图像,它会减少碰撞之间的空间。

enter image description here

答案 1 :(得分:2)

您正在游戏的绘图上下文中使用imageData,因此,由于您有背景,因此根本没有透明像素,因此您的像素碰撞检测始终为真 - &gt;事实上,你只是在做一个边界框检查 算法的想法是比较两个只需要计算一次的静态imageData(getImageData是一个代价高昂的操作)。

一些建议:
•在启动游戏之前加载图像 •redim(裁剪)你的形象,它有很多空白,正如@Quantumplate注意到的那样 •在游戏启动之前,只在上下文中计算一次精灵的imageData。不要忘记在drawImage + getImageData之前使用clearRect()画布。这是解决你的bug的方法 •摆脱

if (xDiff < 4 && yDiff < 4) {

和相应的其他。这种“优化”毫无意义。使用像素检测的要点是精确的。 Redim(裁剪)你的形象更重要的是赢得很多时间(但你需要...... ??)
•Rq:像素检测算法写得多差! 1)绕数字,它正在使用!! 5种不同的方法(圆形,&lt;&lt; 0,~~,0 |,?:)!!! 2)当CPU缓存首先优先于Y时,它首先在X上循环,还有许多其他的东西......但是现在如果有效......

答案 2 :(得分:2)

正如@GameAlchemist指出你正在从画布背景中获取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。即使在做了碰撞算法之后似乎也没有那么好。

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

HTML文件id=testCanvas中有一个新画布。这用于获取怪物和英雄的图像数据值。

以下是修改后代码的plunker link

答案 3 :(得分:1)

这是另一种(更有效的)像素完美碰撞测试...

准备:对于要测试碰撞的每个图像

  • 如上所述,修剪图像边缘多余的透明像素
  • 将画布大小调整为图像大小,(可以为多个图像重复使用1个画布)
  • 在画布上绘制图像
  • 获取画布的所有像素信息: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
        ));
    }
    
  • 计算2幅图像的交叉矩形

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