现在,@balexandre 已经很好地解释了您的代码的一些要点,让我们来看看我们如何计算碰撞。
想象2个范围相互重叠(范围a部分重叠范围b)
[100 .|.. 300]
[200 ..|. 400]
重叠部分来自 | 到 | -> 200 到 300,所以重叠的大小是 100
如果您查看这些数字,您会注意到,可以看到重叠,
- 取右边较小的数 -> 300
- 取左边的最大数 -> 200
- 将它们彼此相减 -> 300 - 200 = 100。
让我们看看另外两种情况。(范围b完全在范围a中)
[50 ... 150]
[75...125]
所以我们的值是:Math.min (150,125) //125
对于结束值和Math.max (50,75) // 75
开始值,导致重叠的值为 125 - 75 = 50
我们来看最后一个例子(Range a not in Range b)
[50 ... 150]
[200 ... 300]
使用上面的公式,得出的结果Math.min (150 , 300 ) - Math.max (50,200) // -50
是绝对值是 2 个范围之间的差距,50
现在我们可以添加最后一个条件,因为您想计算碰撞,> 0
我们只对值感兴趣。鉴于此,我们可以将其置于一种条件下。
Math.min ((Brick["Right"],Ball["Right"]) - Math.max (Brick["Left"], Ball["Left"]) > 0)
true
如果元素重叠并且false
不重叠,这将产生。
将此应用于您的代码,我们可以通过以下方式计算碰撞
bricksCollision: function () {
for (var i = 0; i < $bricks.length; i++) {
var $brick = $($bricks[i]);
var offset = $brick.offset();
var brickBounds = [offset.left - field.l]; //brick left pos
brickBounds[1] = brickBounds[0] + 40 //bricks right pos -> left pos + .bricks.width;
var ballBounds = [ball.l]; //balls left pos
ballBounds[1] = ballBounds[0] + 20 //balls right pos -> left pos + #ball.width;
if (ball.t <= (offset.top + 20) && (Math.min(brickBounds[1], ballBounds[1]) - Math.max(brickBounds[0], ballBounds[0])) > 0) {
$bricks[i].style.opacity = 0; //Make the brick opaque so it is not visible anymore
$bricks.splice(i, 1) //remove the brick from the array -> splice on the array, not the element
return true;
}
}
}
有了这个,我们可以在 Ball 与 Brick 碰撞时返回 true 到 move 函数。
但是,嘿,我们希望它朝着正确的方向反弹,所以我们将面临另一个问题。因此,与其返回 Brick 是否碰撞的布尔值,不如返回一个新的 Ball 应该移动的方向。
为了能够轻松地仅更改方向的 x 或 y 部分,我们应该使用向量之类的东西。
为此,我们可以使用 2 位整数,其中位b0用于 x 方向,位b1用于 y 方向。这样。
Dec Bin Direction
0 -> 00 -> Down Left
^ -> Left
^ -> Down
1 -> 01 -> Down Right
^ -> Right
^ -> Down
2 -> 10 -> Up Left
^ -> Left
^ -> Up
3 -> 11 -> Up Right
^ -> Right
^ -> Up
但是为了能够只改变一部分方向,我们需要将旧方向传递给碰撞函数,并分别使用按位&
和|
来关闭或打开它们
我们还必须计算球从哪一侧碰撞。幸运的是,我们之前有重叠计算,它已经使用了我们需要的所有值来计算碰撞方向。
如果它来自
- 正确的
- Brick ["Right"] - Ball["Left"] 必须与重叠的值相同。
- 剩下
- Ball ["Right"] - Brick["Left"] 必须与重叠的值相同。
如果它们都不是真的,它必须要么来自
- 底部
- 如果 Ball["Top"] 大于 ( Brick["Top"] 加上一半的 Brick["height"] )
或者从顶部。
为了减少从侧面碰撞的条件评估为真的范围,我们可以添加另一个条件,即重叠必须小于例如... && overlap < 2
因此,如果它与边缘碰撞,它并不总是弹到一边。
说了这么多,在代码中这可能看起来像这样。
bricksCollision: function (direction) {
var newDirection = direction
var ballBounds = [ball.l]; //balls left pos
ballBounds[1] = ballBounds[0] + 20 //balls right pos -> left pos + #ball.width;
for (var i = 0; i < $bricks.length; i++) {
var $brick = $($bricks[i]);
var offset = $brick.offset();
var brickBounds = [offset.left - field.l]; //brick left pos
brickBounds[1] = brickBounds[0] + 40 //bricks right pos -> left pos + .bricks.width;
var overlap = Math.min(brickBounds[1], ballBounds[1]) - Math.max(brickBounds[0], ballBounds[0]);
if (ball.t <= ((offset.top - field.t) + 20) && overlap > 0) {
$bricks[i].style.opacity = 0; //Make the brick opaque so it is not visible anymore
$bricks.splice(i, 1) //remove the brick from the array -> splice on the array, not the element
if (ballBounds[1] - brickBounds[0] == overlap && overlap < 2) { //ball comes from the left side
newDirection &= ~(1); //Turn the right bit off -> set x direction to left
} else if (brickBounds[1] - ballBounds[0] == overlap && overlap < 2) { //ball comes from the right side
newDirection |= 1; // Turn the right bit on -> set x direction to right;
} else {
if (ball.t > (offset.top + (20 / 2))) //Ball comes from downwards
newDirection &= ~(2) // Turn the left bit off -> set y direction to down;
else //Ball comes from upwards
newDirection |= 2; // Turn the left bit on -> set y direction to up;
}
//console.log("Coming from: %s Going to: %s", field.directionsLkp[direction], field.directionsLkp[newDirection], direction)
return newDirection;
}
}
return direction;
}
为了让它工作,我们还应该改变moveXX
函数,使用新的方向,返回。
但是如果我们无论如何要从碰撞函数中获得新的方向,我们可以将完整的碰撞检测移动到函数中,以简化我们的移动函数。但在此之前,我们应该看一下移动函数,并添加一个查找对象来field
保存方向的数字,以保持可读性。
var field = {
directions: {
uR : 3, // 11
dR : 1, // 01
dL : 0, // 00
uL : 2 // 10
},
directionsLkp: [
"dL","dR","uL","uR"
],
...
}
现在移动功能看起来像这样,
ballCondact: function () {
var moves = [moveDl,moveDr,moveUl,moveUr]
var timeout = 5;
function moveUr() {
var timer = setInterval(function () {
$ball.css({
top: (ball.t--) + "px",
left: (ball.l++) + "px"
})
var newDirection = game.bricksCollision(field.directions.uR) //get the new direction from the collision function
if (newDirection !== field.directions.uR) {
clearInterval(timer);
moves[newDirection](); //move in the new direction
}
}, timeout);
}
...
}
像这样,如果碰撞函数返回的方向与当前方向不同,则移动函数只是改变方向。
现在我们可以开始将墙壁碰撞移动到碰撞函数中,为此我们可以在开始时添加另一个检查。
bricksCollision: function (direction) {
...
if (ball.t <= field.t)
newDirection &= ~(2); //Ball is at top, move down
else if (ball.l <= 0) //Ball is at the left, move right
newDirection |= 1;
else if (ball.t >= field.b - ball.height) //Ball is at the bottom, move up
newDirection |= 2;
else if (ball.l > field.width - ball.width) //Ball is at the right, move left
newDirection &= ~(1);
if (direction !== newDirection)
return newDirection
...
}
注意,我省略了平台的碰撞检查,因为这个想法应该很清楚=)
这是一个小提琴