javascript - 为什么球会穿过砖 block ?

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

我正在用 javaScript 编写一个 Arkanoid 克隆,但我遇到了球/砖碰撞问题。这就是我进行渲染的方式(这可能很重要,我不知道):

function Game() {
//...
this.fps = 60;
//...
}

Game.prototype.gameloop = function() {

var current = new Date().getTime(),
        delta = current - this.lastRender;

if (delta >= this.delay && !this.paused) {
    this.updateAll();
    if (this.paused)
        return;
    this.drawAll();
    this.lastRender = new Date().getTime();
}
this.loop = requestAnimationFrame(Game.prototype.gameloop.bind(this));
};

我通过简单地调用 this.gameloop(); 在 Game 构造函数中启动这个循环。

我在游戏循环的每次迭代中执行的检测碰撞的算法:

  1. 我计算最下面一排砖 block 的高度 (brick.height*rows); 每次我移动球(我只是通过 ball.x +=ball.xVelocity; ball.y+=ball.yVelocity; 顺便说一句),我检查球的“y”坐​​标是否较小那么这个bricksLevel。 ->
  2. 如果在第 1 步中检查返回“真”,我会寻找最近的砖 block :

    var rowTop = Math.floor(ball.y / height), rowBot = Math.floor(ball.bottom / height),
        colLeft = Math.floor(ball.x / width),
        colRight = Math.floor(ball.right / width);
    

    如果这些积木存在于 BricksCollection 对象中,我会得到这些积木:

    var suspects = bricks.slice({ 行:[rowTop,rowBot], cols: [colLeft, colRight] }); 对于它们中的每一个,我都应用我的球/砖碰撞检测算法,直到它返回 true(这意味着碰撞发生):

    var i = 0;
    while (
    suspects[i] &&
    !this.detectBrickCollision(ball, suspects[i])
    && i < suspects.length) {
    i++;
    }
    
  3. 在 detectBrickCollision 中存在用于实际确定是否发生碰撞以及碰撞是否发生在 block 的哪一侧的代码。起初我想出了比数学更合乎逻辑的方法。在四个连续的 if block 中,我检查了矩形的每一边是否有碰撞。像那样:

      if (ball.yVelocity < 0 && !brick.below && ball.y <= brick.bottom  && ball.center.x <= brick.right && ball.center.x >= brick.x) {
          ball.yVelocity = -ball.yVelocity;
          ball.y = brick.bottom;
          brick.collide(ball);
          return true;
      }
      if (//similar checks for other sides)
      //...
    

    这就是它的工作原理:http://jsfiddle.net/78NJ6/ 请注意,球与砖 block 的底部碰撞得很好,与左侧和右侧的碰撞也相当好,但与顶部的碰撞更糟,尽管所有侧面的逻辑几乎相同!例如,这里是顶部碰撞检查的代码:

      if (ball.yVelocity > 0 && !brick.above && !brick.isUpper() && ball.bottom >=  brick.y && (ball.x <= brick.right || ball.right >= brick.x)) {
          ball.yVelocity = -ball.yVelocity;
          ball.y = brick.y - ball.height;
          brick.collide(ball);
          return true;
      }
    

现在,如果您从上方尝试 fiddle ,您可能已经注意到,在顶部碰撞的情况下,球会远远低于砖 block 的上边缘。有时它甚至达到底部边缘。显然,这还不够好。我寻找其他方法来检测碰撞,最终我遇到了这个解决方案:https://stackoverflow.com/a/1879223/3149635

我用以下代码替换了 detectBrickCollision 函数中的代码:

 var intersects = this.intersects(ball, brick);
 if (intersects) {
 switch (intersects) {
     case "top":
         ball.y = brick.y-ball.height;
         ball.yVelocity = - ball.yVelocity;
         break;
     case 'bottom':
         ball.y=brick.bottom;
         ball.yVelocity = -ball.yVelocity;
         break;
     case 'left':
         ball.x = brick.x-ball.width;
         ball.xVelocity = - ball.xVelocity;
         break;
     case 'right':
         ball.x = brick.right;
         ball.xVelocity = -ball.xVelocity;
         break;
 }
 brick.collide(ball);
 return true;
 }
 return false;
 };

我放在函数中的所有数学运算都相交。我将在此处展示完整内容,但这基本上就是我在上面这个答案中看到的内容:

CollisionResolver.prototype.intersects = function(ball, brick) {
//find closest point on the rectangle's perimeter
var closestX = this.clamp(ball.center.x, brick.x, brick.right),
        closestY = this.clamp(ball.center.y, brick.y, brick.bottom);

//calculate the distance between this closest point and circle's center
var dx = ball.center.x - closestX,
        dy = ball.center.y - closestY;

//if the distance is less than circle's radius, an intersection occurs
var d = Math.sqrt((dx * dx) + (dy * dy));

if (d < ball.radius) {
    //determin which side of rectangle collided
    var side;
    if (closestX === brick.x)
        side = "left";
    else if (closestX === brick.right)
        side = 'right';
    else if (closestY === brick.y)
        side = 'top';
    else if (closestY === brick.bottom)
        side = 'bottom';
    return side;
}
return false;
};

尽管这应该更加精确和整洁,但结果并不是很好。现在球仍然从底部真实地反射,从右/左以某种方式正常反射。顶部碰撞变得更糟。如果它来自上方,有时它会飞过砖 block 。新版本:http://jsfiddle.net/X4MuW/

我很绝望。我不明白 - 为什么这两种方法都能轻松处理底部、右侧和左侧的碰撞,但当涉及到砖 block 的顶部边缘时却SUCK?上面有什么特别之处?请给我一些提示,它可能是什么。我快死在这里了。

最佳答案

嘿!我在 CollisionResolver.prototype.conduct 中找到了它:

rowBot = Math.floor(ball.bottom / height)

您需要为 rowBot 汇总:

rowBot = Math.ceil(ball.bottom / height)

http://jsfiddle.net/78NJ6/8/

还有:

当球从墙上弹起时,您需要重新设置球的 x 坐标,否则它可能会“卡在”墙上。 (如果它在从 bate 反弹时移动到墙上并且 x 速度降低因此它不会在下一步移动中再次移出,则会发生这种情况。)

关于javascript - 为什么球会穿过砖 block ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23157531/

相关文章:

javascript - 如何限制每行的文本区域长度(以像素为单位)和行数?

javascript - Python Flask 在 javascript 文件中使用 config.py 中的变量

python - 如何修复错误嵌套/未闭合的 HTML 标签?

javascript - 在 Canvas 上绘制时的偏移

c - 如何返回 true 或 false 的相反值?

java - 在图中使用循环寻路(每条边使用一次)

angular - 使用 ctx.putImageData 更改图像大小

javascript - HTML: 多个 onclick 音频文件,让最后一个停止

javascript - 使用 Bluebird 链接多个请求

javascript - Firebase错误: [code=resource-exhausted]: Write stream exhausted maximum allowed queued writes