我正在用 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 构造函数中启动这个循环。
我在游戏循环的每次迭代中执行的检测碰撞的算法:
- 我计算最下面一排砖 block 的高度 (brick.height*rows);
每次我移动球(我只是通过
ball.x +=ball.xVelocity; ball.y+=ball.yVelocity;
顺便说一句),我检查球的“y”坐标是否较小那么这个bricksLevel。 -> 如果在第 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++; }
在 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)
还有:
当球从墙上弹起时,您需要重新设置球的 x 坐标,否则它可能会“卡在”墙上。 (如果它在从 bate 反弹时移动到墙上并且 x 速度降低因此它不会在下一步移动中再次移出,则会发生这种情况。)
关于javascript - 为什么球会穿过砖 block ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23157531/