Javascript 基于阵列的碰撞检测

Javascript 基于阵列的碰撞检测,javascript,jquery,Javascript,Jquery,大家好我开始写一个关于球和砖块的小游戏,在碰撞检测方面遇到了一些问题。这是我的密码。我知道通过数组检测是有效的,但我不知道如何将它应用到我的代码中 以下是我尝试过的: bricksCollision: function() { for (var i = 0; i < $bricks.length; i++) { if ($ball.t == $bricks[i].offset().top) { $br

大家好
我开始写一个关于球和砖块的小游戏,在碰撞检测方面遇到了一些问题。这是我的密码。我知道通过数组检测是有效的,但我不知道如何将它应用到我的代码中

以下是我尝试过的:

 bricksCollision: function() {
        for (var i = 0; i < $bricks.length; i++) {
                if ($ball.t == $bricks[i].offset().top) {
                    $bricks[i].splice(i, 1);
                }
        }
bricksCollision:function(){
对于(变量i=0;i<$bricks.length;i++){
if($ball.t==$bricks[i].offset().top){
$bricks[i]。拼接(i,1);
}
}
游戏中的每一块砖块都由for循环生成,然后进入$bricks数组。生成后的每一块砖块都接收顶部和左侧位置,并具有绝对位置。我尝试检查$ball.t(检测球顶部位置的球对象的属性)是否到达砖块,然后移除砖块

谢谢你的帮助。我才刚刚开始学习JS,这就是为什么我的代码是
棘手。

首先,让我们跟踪一些代码错误

  • $ball.t
    可能应该是
    $ball.top
  • 您不需要将
    $
    作为前缀,对于您的代码,它只是一个变量,您正在调用
    $ball
    ,而不是
    ball
    ,这将导致假设错误
对于这些假设错误,这里是您做错的地方:

  • $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元素调用方法。

现在,@balexandre已经很好地解释了有关代码的一些要点,让我们看看如何计算冲突

想象两个范围相互重叠(范围a部分重叠范围b)

零件重叠从|到|->200到300,因此重叠的大小为100

如果你看这些数字,你会注意到,重叠部分可以被看作是

  • 取右侧较小的数字->300
  • 取左侧最大号->200
  • 相互减去->300-200=100
让我们看看另外两种情况。(范围b完全在范围a中)

因此,我们得到的值是:
Math.min(150125)//125
用于结束值,而
Math.max(50,75)//75
用于开始值,这导致重叠的值为125-75=50

让我们看一下最后一个示例(范围a不在范围b内)

使用上述公式,得出结果
Math.min(150300)-Math.max(50200)/-50
,其中absolutes的值是两个范围之间的间隙,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;
        }
    }
}
但是为了只改变方向的一部分,我们需要将旧的方向传递给碰撞函数,并分别使用按位
&
|
来关闭或打开它们

我们还要计算球从哪一边碰撞。 幸运的是,我们有以前的重叠计算,它已经使用了我们需要的所有值来计算碰撞方向

如果它来自

    • 砖[“右”]-球[“左”]的值必须与重叠的值相同
    • 球[“右”]-砖[“左”]的值必须与重叠的值相同
如果没有一个是真的,那么它要么来自

  • 底部
    • 如果球[“顶部”]大于(砖[“顶部”]加上砖的一半[“高度”])
或者从顶部开始

为了减小侧面碰撞条件计算为真的范围,我们可以添加另一个条件,即重叠必须小于,例如
…&&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;
}
现在,移动函数可以如下所示

    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);
        }
...
}
与此类似,如果碰撞函数返回与当前方向不同的方向,则move函数只需更改方向

现在我们可以开始将墙碰撞移动到碰撞函数,为此,我们可以在开始处添加另一个检查

    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

      ...
}
砖块碰撞:功能(方向){
...
如果(ball.t field.width-ball.width)//ball位于右侧,则向左移动
新方向&=~(1);
如果(方向!==newDirection)
返回新方向
...
}
注意,我忽略了平台的碰撞检查,因为想法应该很清楚=)


这里有一个

只是一个注释,JavaScript不像某些人那样强制执行
$varName
约定。你可以调用你的bricks数组
bricks
,而不是
$bricks
。如果我理解正确,你只是在比较顶部值和左侧值。这意味着
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
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;
}
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

      ...
}