Как проверить, перекрывается ли div с другим div каждую секунду?

#javascript #foreach #collision #event-listener #overlapping

#javascript #foreach #столкновение #прослушиватель событий #перекрытие

Вопрос:

Я делаю быструю игру о перемещении игрока, чтобы уклоняться от пуль, но у меня возникают проблемы с тем, как определить, столкнулась ли какая-либо пуля с игроком. В игровых движках обычно просто существуют eventlisteners для объединения двух объектов, поскольку я не нашел ничего подобного для разработки webb, я подумал, что могу просто использовать бесконечный цикл для проверки каждого маркера, если он столкнулся с игроком.

Я не могу выполнить while (true) цикл, потому что он просто вылетает. Я попытался создать интервал в функции spawn для каждого маркера, но я не мог понять, как очистить интервал после удаления маркера. И я также попытался просто создать интервал, который бы перебирал существующие маркеры в данный момент и проверял, не пересекается ли пуля, но по какой-то причине я получаю эту ошибку: Uncaught TypeError: bullets.forEach is not a function вот код:

 
function checkOverlap(){
    let bullets = document.getElementsByClassName("bullet");
    if (bullets.length > 0){
        bullets.forEach(bullet => {
            let overlap = !(bullet.right < player.left || 
                    bullet.left > player.right || 
                    bullet.bottom < player.top || 
                    bullet.top > player.bottom)
            if (overlap){
                console.log("overlap");
            }
        });
    }
}
setInterval(checkOverlap, 1000);
 

Мне также интересно, возможно ли вообще обнаружить перекрытие таким образом. Поскольку я использую transition left, top, right, down свойства и css для перемещения моих маркеров, будет ли это обнаружено или мне нужно использовать что-то еще?

Комментарии:

1. Вы отметили столкновение

2. getElementsByClassName возвращает HTMLCollection объект, у которого нет forEach метода (и является прямой ссылкой на DOM). Попробуйте querySelectorAll('.bullet') вместо этого статический NodeList

Ответ №1:

Вы не можете получить доступ forEach , потому document.getElementsByClassName что не возвращает an Array — он возвращает a NodeList , который ведет себя примерно так же, как an Array , но отличается другими способами — во-первых, его прототип не имеет forEach метода.

Вы можете легко преобразовать в an Array , выполнив вместо этого следующее:

 let bullets = [ ...document.getElementsByClassName('bullet') ];
 

Что касается столкновения, я рекомендую вам использовать getBoundingClientRect (я часто путал его написание getClientBoundingRect , теперь я напоминаю себе, что B предшествует C : D). Это возвращает абсолютную геометрию html-элемента с учетом всего:

 let doCollide = (player, bullet) => {
  let playerBound = player.getBoundingClientRect();
  let bulletBound = bullet.getBoundingClientRect();

  // (px, py) and (bx, by) represent the center of the player and bullet
  let px = playerBound.left   playerBound.width * 0.5;
  let py = playerBound.top   playerBound.height * 0.5;
  let bx = bulletBound.left   bulletBound.width * 0.5;
  let by = bulletBound.top   bulletBound.height * 0.5;

  let wOff = (playerBound.width   bulletBound.width) * 0.5;
  let hOff = (playerBound.height   bulletBound.height) * 0.5;

  // Collision occurs when bullet and player are separated on neither x nor y axes
  return Math.abs(px - bx) < wOff amp;amp; Math.abs(py - by) < hOff;
}

let player = document.getElementsByClassName('player')[0];
let bullet = document.getElementsByClassName('bullet')[0];

let testFn = () => {

  player.style.left = `${Math.floor(Math.random() * 300)}px`;
  player.style.top = `${Math.floor(Math.random() * 150)}px`;
  bullet.style.left = `${Math.floor(Math.random() * 300)}px`;
  bullet.style.top = `${Math.floor(Math.random() * 150)}px`;
  
  if (doCollide(player, bullet)) {
    player.classList.add('coll');
    bullet.classList.add('coll');
  } else {
    player.classList.remove('coll');
    bullet.classList.remove('coll');
  }
  
};

setInterval(testFn, 1500);
testFn(); 
 html, body { position: relative; width: 100%; height: 100%; overflow: hidden; padding: 0; margin: 0; }

.player, .bullet { position: absolute; }
.player { width: 150px; height: 150px; box-shadow: inset 0 0 0 4px rgba(0, 150, 0, 1); }
.bullet { width: 60px; height: 90px; box-shadow: inset 0 0 0 4px rgba(0, 210, 0, 1); }

.player.coll { box-shadow: inset 0 0 0 4px rgba(200, 0, 0, 1); }
.bullet.coll { box-shadow: inset 0 0 0 4px rgba(255, 0, 0, 1); } 
 <div class="player"></div>
<div class="bullet"></div>