#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>