大家好,我开始用球和砖写一个小游戏,碰撞检测有问题。这是我的代码http://jsbin.com/ibufux/9。我知道检测通过数组工作,但我无法想象如何将其应用于我的代码。
以下是我的尝试:
bricksCollision: function() {
for (var i = 0; i < $bricks.length; i++) {
if ($ball.t == $bricks[i].offset().top) {
$bricks[i].splice(i, 1);
}
}
游戏中的每个砖块都是由for循环生成的,然后转到$ bricks阵列。生成后的每一块砖都会收到顶部和左侧位置并且位置绝对。我试图检查$ ball.t(它是我检测球顶位置的球对象的属性)是否到达砖块而不是去除砖块。
感谢您的帮助。我只是开始学习JS,这就是为什么我的代码是 棘手。
答案 0 :(得分:5)
首先,让我们来谈谈一些代码错误
$ball.t
可能应该是$ball.top
$
作为前缀,对于您的代码,它只是一个变量,而您调用$ball
代替ball
会导致假设错误强> <!/ LI>
对于那些假设错误,这就是你做错了:
$ball
是一个dom元素,而不是jQuery元素$bricks
$ball
是一个数组从某些console.log()
得出的结论让我们尝试修复代码:
$ball
,因为它的数组元素只有$ball[0]
,因为你有变量指向DOM元素而不是jQuery元素,你需要将它包装在Jquery中:
if ( $($ball[0]).top === $($bricks[i]).offset().top ) { ...
一个不要混淆的好主意只是在jQuery元素中使用$
,在变量前加前缀,不会使它们成为jQuery元素。
每次当你看到你有一个错误,例如“元素x没有方法y”时,总是假设你从DOM元素调用方法,而不是jQuery元素。 / p>
答案 1 :(得分:2)
现在,@ balexandre很好地解释了有关代码的一些要点,让我们来看看如何计算碰撞。
想象一下2个范围相互重叠(范围 a 部分重叠范围 b )
[100 .|.. 300]
[200 ..|. 400]
重叠的部分来自|到| - &GT; 200至300,因此重叠的大小为100
如果你看一下这些数字,你会注意到重叠可以看作是,
让我们看看其他两种情况。 (范围 b 完全在范围 a )
[50 ... 150]
[75...125]
所以我们得到的值是:Math.min (150,125) //125
表示结束值,Math.max (50,75) // 75
表示起始值,结果重叠值为125 - 75 = 50
让我们来看看最后一个例子(范围 a 不在范围 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到移动函数。
但是,嘿,我们希望它朝着正确的方向反弹,所以我们将面临另一个问题。 因此,无论砖块是否发生碰撞,我们都会返回一个布尔值,我们可以返回一个新的方向,即球将移动。为了能够轻松改变方向的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
但是为了能够仅改变方向的一部分,我们需要将旧方向传递给碰撞函数,并分别使用按位&
和|
将它们关闭或打开< / p>
此外,我们必须计算球碰撞的哪一侧。 幸运的是,我们之前有重叠计算,它已经使用了我们需要的所有值来计算碰撞方向。
如果来自
如果它们都不是真的,那么它必须来自
或者从顶部开始。
为了减少条件(从侧面碰撞)评估为真的范围,我们可以添加另一个条件,即重叠必须小于例如... && 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
...
}
注意,我省略了平台的碰撞检查,因为想法应该清楚=)
这是Fiddle