使用纯 jQuery 的碰撞检测未提供所需的输出

Collision detection using pure jQuery is not giving desired output

我正在尝试开发一个非常简单的游戏,当用户点击操场。

有一些移动的墙壁(黑盒子)作为障碍物,船应该避免与之碰撞。

如果发生任何碰撞,墙壁将停止移动,并在控制台中打印出一条文本。

我已经尽可能接近它了。 但它有时会起作用,但并非总是如此。你可以在下面的代码中看到它,尝试与墙碰撞。有时它会阻止它们并打印文本,有时它会忽略碰撞,就好像什么都没发生一样。

我不知道为什么会这样。

这是代码。

$('document').ready(function() {

  var $totalHeight = $('.inner').height(); //of walls
  var $maxHeight = Math.ceil(Math.ceil($totalHeight / 3) - (Math.ceil($totalHeight / 3) * 30) / 100); //30% of total wall height

  $('.wall').each(function(i, obj) {
    $(this).height($maxHeight);
    $('.wall.four').css({
      'height': $wallGap
    });
  })

  var $wallGap = Math.ceil($totalHeight / 3) - $maxHeight;
  var $wallOneTop = 0;
  var $wallTwoTop = $maxHeight + $wallGap;
  var $wallThreeTop = ($maxHeight * 2) + ($wallGap * 2);
  var $wallFourTop = -$('.wall.four').height() - $wallGap;

  $('.wall.one').css({
    'top': $wallOneTop
  });
  $('.wall.two').css({
    'top': $wallTwoTop
  });
  $('.wall.three').css({
    'top': $wallThreeTop
  });
  $('.wall.four').css({
    'top': $wallFourTop
  });


  function moveWall(wallObj) {
    var $currentTop = wallObj.position().top;
    var $limitTop = $('.inner').height();

    if ($currentTop >= $limitTop) {
      var $rand = Math.floor(Math.random() * ($maxHeight - $wallGap + 1) + $wallGap);
      wallObj.height($rand);
      var $top = -(wallObj.height());
    } else {
      var $top = (wallObj.position().top) + 5;
    }
    var $collide = checkCollision(wallObj);
    wallObj.css({
      'top': $top
    });
    return $collide;
  }

  var $wallTimer = setInterval(function() {
    $('.wall').each(function(i, obj) {
      var $status = moveWall($(this));
      if ($status == true) {
        clearInterval($wallTimer);
      }
    })
  }, 40);


  function checkCollision(wallObj) {
    var $ship = $('.ship');
    var $shipWidth = $ship.width();
    var $shipHeight = $ship.height();
    var $shipLeft = $ship.position().left;
    var $shipRight = $shipLeft + $shipWidth;
    var $shipTop = $ship.position().top;
    var $shipBottom = $shipTop + $shipHeight;

    var $wall = wallObj;
    var $wallWidth = wallObj.width();
    var $wallHeight = wallObj.height();
    var $wallLeft = wallObj.position().left;
    var $wallRight = $wallLeft + $wallWidth;
    var $wallTop = wallObj.position().top;
    var $wallBottom = $wallTop + $wallHeight;

    if (
      $shipLeft >= $wallRight ||
      $shipRight <= $wallLeft ||
      $shipTop >= $wallBottom ||
      $shipBottom <= $wallTop
    ) {
      return false;
    } else {
      console.log("dhumm!");
      return true;
    }
  }







  $('.outer .inner').click(function() {
    var $ship;
    $ship = $('.ship');
    $shipLeft = $ship.position().left;
    $shipRight = $shipLeft + $ship.width();

    $inner = $('.inner');
    $innerLeft = $inner.position().left;
    $innerRight = $innerLeft + $inner.width();


    if (($shipLeft < $inner.width() - $ship.width())) {
      $ship.animate({
        "left": $inner.width() - $ship.width()
      }, 500, "linear");
    } else if (($shipRight >= $inner.width())) {
      $ship.animate({
        "left": '0'
      }, 500, "linear");
    }

  });


});
.outer {
  background: #fff;
  border: 20px solid #efefef;
  width: 400px;
  height: 600px;
  display: inline-block;
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  margin: auto;
  overflow: hidden;
}

.outer .inner {
  background: #fff;
  height: 100%;
  width: 100%;
  margin: auto;
  position: relative;
  overflow: hidden;
}

.outer .inner .wall {
  width: 5px;
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  background: #000;
}

.outer .inner .ship {
  width: 15px;
  height: 15px;
  background: red;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="outer">
  <div class="inner">
    <div class="wall one"></div>
    <div class="wall two"></div>
    <div class="wall three"></div>
    <div class="wall four"></div>

    <div class="ship"></div>
  </div>
</div>

正如freefomn-m已经说过的。 检查船的动画循环中的碰撞,而不是墙壁。

为此,我将第二种类型的参数用于 jQuery 的 .animate 方法

.animate( properties, options )

我使用"progress"选项来检查船舶每个移动周期的碰撞。

console.clear();
$('document').ready(function() {

  var collided = false;
  var collidedWith = null;
  var $ship = $('.ship');
  var $walls = $('.wall')
  
  var $totalHeight = $('.inner').height(); //of walls
  var $maxHeight = Math.ceil(Math.ceil($totalHeight / 3) - (Math.ceil($totalHeight / 3) * 30) / 100); //30% of total wall height

  $('.wall').each(function(i, obj) {
    $(this).height($maxHeight);
    $('.wall.four').css({
      'height': $wallGap
    });
  })

  var $wallGap = Math.ceil($totalHeight / 3) - $maxHeight;
  var $wallOneTop = 0;
  var $wallTwoTop = $maxHeight + $wallGap;
  var $wallThreeTop = ($maxHeight * 2) + ($wallGap * 2);
  var $wallFourTop = -$('.wall.four').height() - $wallGap;

  $('.wall.one').css({
    'top': $wallOneTop
  });
  $('.wall.two').css({
    'top': $wallTwoTop
  });
  $('.wall.three').css({
    'top': $wallThreeTop
  });
  $('.wall.four').css({
    'top': $wallFourTop
  });


  function moveWall(wallObj) {
    var $currentTop = wallObj.position().top;
    var $limitTop = $('.inner').height();

    if ($currentTop >= $limitTop) {
      var $rand = Math.floor(Math.random() * ($maxHeight - $wallGap + 1) + $wallGap);
      wallObj.height($rand);
      var $top = -(wallObj.height());
    } else {
      var $top = (wallObj.position().top) + 5;
    }
    //    var $collide = checkCollision(wallObj);
    wallObj.css({
      'top': $top
    });
    // return $collide;
  }

  var $wallTimer = setInterval(function() {
    $walls.each(function(i, obj) {
      moveWall($(this));
      if (collided) {
        clearInterval($wallTimer);
      }
    })
  }, 40);


  function checkCollision() {
    var $shipWidth = $ship.width();
    var $shipHeight = $ship.height();
    var $shipLeft = $ship.position().left;
    var $shipRight = $shipLeft + $shipWidth;
    var $shipTop = $ship.position().top;
    var $shipBottom = $shipTop + $shipHeight;
    
    $('.wall').each(function(i) {

      var $wall = $(this);
      var $wallWidth = $wall.width();
      var $wallHeight = $wall.height();
      var $wallLeft = $wall.position().left;
      var $wallRight = $wallLeft + $wallWidth;
      var $wallTop = $wall.position().top;
      var $wallBottom = $wallTop + $wallHeight;

      if (
        $shipLeft < $wallRight &&
        $shipRight > $wallLeft &&
        $shipTop < $wallBottom &&
        $shipBottom > $wallTop
      ) {
        console.log("dhumm!");
        collided = true;
        collidedWith = $wall
        $wall.addClass('crashed')
        $ship.addClass('crashed')
        $ship.stop();
        return false;
      }
    })
  }







  $('.outer .inner').click(function() {
    var $ship;
    $ship = $('.ship');
    $shipLeft = $ship.position().left;
    $shipRight = $shipLeft + $ship.width();

    $inner = $('.inner');
    $innerLeft = $inner.position().left;
    $innerRight = $innerLeft + $inner.width();


    if (($shipLeft < $inner.width() - $ship.width())) {
      $ship.animate({
        "left": $inner.width() - $ship.width()
      }, {
        "duration": 500,
        "easing": "linear",
        "progress": checkCollision,
      });
    } else if (($shipRight >= $inner.width())) {
      $ship.animate({
        "left": '0'
      }, {
        "duration": 500,
        "easing": "linear",
        "progress": checkCollision,
      });
    }

  });


});
.outer {
  background: #fff;
  border: 20px solid #efefef;
  width: 400px;
  height: 600px;
  display: inline-block;
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  margin: auto;
  overflow: hidden;
}

.outer .inner {
  background: #fff;
  height: 100%;
  width: 100%;
  margin: auto;
  position: relative;
  overflow: hidden;
}

.outer .inner .wall {
  width: 5px;
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  background: #000;
}
.outer .inner .wall.crashed {
  background: red;
}


.outer .inner .ship {
  width: 15px;
  height: 15px;
  background: orange;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
}

.outer .inner .ship.crashed {
  background: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="outer">
  <div class="inner">
    <div class="wall one"></div>
    <div class="wall two"></div>
    <div class="wall three"></div>
    <div class="wall four"></div>

    <div class="ship"></div>
  </div>
</div>

作为建议,我将如何从头开始。

使用由 setIntervalsetTimeout 调用的更新周期,或者更好的是 requestAnimationFrame。更新周期将负责时间进度并协调不同的对象。结构是这样的。

jQuery(function($) { // same as $('document').ready()
  var ship = ...;
  var boundaries = ...;
  var walls = ...;
  var clickEvents = [];

  document.addEventListener('click', function(e) {clickEvents.push(e)})
  var handleEvents = function() {}
  var setupWalls = function () {}
  var setupShip= function () {}
  var moveWalls = function () {}
  var moveShip = function () {}
  var checkCollision() {}

  var setup = function() {
    setupWalls();
    setupShip();
    // set the initial positions of the ships and the walls
  }

  var update = function() {
     handleEvents();
     moveWalls();
     moveShips();
     var collided = checkCollision();
     if (!collided) {
         setTimeout(update, 30);
     }
  }
  setup();
  update();
})