【问题标题】:Pixel Collision Detection Not Working像素碰撞检测不起作用
【发布时间】:2015-12-26 14:39:30
【问题描述】:

这是我的游戏plnkr

(编辑:另一个plnkr 有一个静态怪物而不是多个动态怪物)

Enter 否则按钮将重新开始游戏。

谁能说出为什么取自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();
        }
    }
}

【问题讨论】:

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


    【解决方案1】:

    正如@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

    【讨论】:

    • 谢谢,做到了 :) 基本上我正在旋转剑,为了简洁起见,我将其注释掉(第 223-224 行而不是有效的 222 行)。当我重新打开它时,它又搞砸了。有什么想法,我是否应该创建一个新问题?
    • MMmmphh !!!但这就是我所说的!但我承认你说得更清楚;-)
    • @OmriAharon :是的,旋转剑正在“破坏”现有的像素碰撞算法。一个新问题会更好。
    • 既然你们已经知道这个游戏了,那就看看吧:stackoverflow.com/questions/34475754/…
    【解决方案2】:

    您的主图尺寸为 71x68 像素,并且在外部有很多透明空间。我猜如果你裁剪它以适合图像,它会减少碰撞之间的空间。

    【讨论】:

      【解决方案3】:

      您正在游戏的绘图上下文中获取 imageData,因此由于您有背景,因此根本没有透明像素,因此您的像素碰撞检测始终返回 true -> 实际上,您只是在进行边界框检查。
      该算法的思想是比较两个只需要计算一次的静态 imageData(getImageData 是一个代价高昂的操作)。

      一些建议:
      • 在启动游戏之前加载您的图片。
      • 重新调整(裁剪)您的图像,它有很多空白,正如@Quantumplate 所注意到的那样。
      • 仅在游戏启动前在上下文中计算一次精灵的 imageData。不要忘记在 drawImage + getImageData 之前清除画布。这是解决您的错误的方法。
      • 摆脱

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

      和相应的 else。这种“优化”毫无意义。使用像素检测的目的是要精确。 Redim(裁剪)您的形象对于赢得很多时间更重要(但您需要... ?? )
      • Rq:像素检测算法写得多么糟糕!!! 1)要舍入一个数字,它正在使用 !! 5 种不同的方法(round,

      【讨论】:

      • 感谢详细的解释!关于算法,我会尝试优化它。我建议你给他们一个评论,听起来你有一些有效的观点。
      【解决方案4】:

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

      准备工作:针对您要测试碰撞的每张图片

      • 如前所述,修剪图像边缘多余的透明像素,
      • 将画布调整为图像大小,(您可以为多张图像重复使用 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
            ));
        }
        
      • 计算两个图像的相交矩形

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

      【讨论】:

      • @Omri Aharon:我没有测试答案的最后一部分,因为这里是凌晨 1 点之后,所以您可能需要调整该代码。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多