I am making a quick game about moving the player to dodge bullets, but I am having problems with how to detect if any bullet colided with the player. In game engines there would usually just exist eventlisteners for coliding of two objects, since I haven't found anything like that for webb development, I thought I could just use an infinite loop to check each bullet if it has collided with the player.
I can't do a while (true)
loop, because it just crashes. I tried to make an interval in the spawn function of each bullets, but I couldn't figure out how to clear the interval after the bullet despawns. And I have also tried to just make an interval that would loop through existing bullets at the moment and check if the bullet is coliding, but for some reason I get this error: Uncaught TypeError: bullets.forEach is not a function
here is the code:
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);
I also wonder if this is even posible to detect the overlapping this way. Since I am using transition
and left, top, right, down
css properties to move my bullets, would it detect or do I need to use something else?
You can't access forEach
because document.getElementsByClassName
doesn't return an Array
- it returns a NodeList
which behaves somewhat like an Array
, but deviates in other ways - for one thing its prototype has no forEach
method.
You can easily convert to an Array
by doing the following instead:
let bullets = [ ...document.getElementsByClassName('bullet') ];
As far as collision goes, I recommend you use getBoundingClientRect
(I often confused its spelling for getClientBoundingRect
, now I remind myself that B
preceeds C
:D). This returns the absolute geometry of an html element, taking everything into account:
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 && 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>