I'm making a simple Space Invaders Clone in Web and ran into an issue. The code works well so far with the exception of the collision system. I need to destroy an enemy every time that the player's bullet hits it. In order to make it, I'm grabbing the x and y coordinates of both the enemies' div and the bullet's div. The issue appears on the values comparison: apparently getBoundingClientRect() grabs the x and y coordinates from within the element excluding its real size. Hence, according to my code, only when the bullet is perfectly inside the enemy that things would trigger. Is there a better way of doing it? How so?
Thanks in advance.
const shoot = (x) => {
//Creates bullet
const main = document.getElementById("main");
const div_bullet = document.createElement("div");
//Appends child
main.appendChild(div_bullet);
//Sets position
div_bullet.style.top = "340px";
div_bullet.style.left = x "px";
//Gives it a class
div_bullet.classList.add("div_bullet");
div_bullet.setAttribute("id", "bullet");
//Deletes bullet
setTimeout(() => {
div_bullet.remove();
}, 1000);
};
const load_game = () => {
//Adding movement to the spaceship
const space_ship = document.getElementById("space_ship");
let x = 375;
window.addEventListener("keydown", (e) => {
switch (e.key) {
case "ArrowLeft":
if (x <= 15) break;
x = x - 10;
space_ship.style.left = x "px";
break;
case "ArrowRight":
if (x >= 725) break;
x = x 10;
space_ship.style.left = x "px";
break;
case "ArrowUp":
if (document.getElementsByClassName("div_bullet").length === 0)
shoot(x 20);
break;
default:
break;
}
});
};
const collision_system = () => {
setInterval(() => {
//Checks if any enemy was hit
const e1 = document.getElementById("e1");
const e2 = document.getElementById("e2");
const e3 = document.getElementById("e3");
const e4 = document.getElementById("e4");
const e5 = document.getElementById("e5");
const enemies_position = [{
x: e1.getBoundingClientRect().x,
y: e1.getBoundingClientRect().y
},
{
x: e2.getBoundingClientRect().x,
y: e2.getBoundingClientRect().y
},
{
x: e3.getBoundingClientRect().x,
y: e3.getBoundingClientRect().y
},
{
x: e4.getBoundingClientRect().x,
y: e4.getBoundingClientRect().y
},
{
x: e5.getBoundingClientRect().x,
y: e5.getBoundingClientRect().y
},
];
if (document.getElementById("bullet")) {
const x_bullet = document
.getElementById("bullet")
.getBoundingClientRect().x;
const y_bullet = document
.getElementById("bullet")
.getBoundingClientRect().y;
console.log(
"X: " enemies_position[0].x "Y: " enemies_position[0].y
);
console.log("X bullet: " x_bullet "Y bullet : " y_bullet);
for (let i = 0; i < 5; i ) {
if (
enemies_position[i].x === x_bullet &&
enemies_position[i].y === y_bullet
)
alert("Shoot!");
}
}
}, 10);
};
document.addEventListener("DOMContentLoaded", () => {
load_game();
collision_system();
});
* {
padding: 0px;
margin: 0px;
}
body {
background-color: black;
width: 100%;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.main {
width: 800px;
height: 500px;
border: 2px white solid;
border-radius: 10px;
}
@keyframes enemies_move {
from {
left: 80px;
}
to {
left: -80px;
}
}
.enemies {
margin-top: 20px;
position: fixed;
width: 800px;
height: 50px;
display: flex;
justify-content: space-evenly;
}
.enemies div {
position: relative;
width: 50px;
height: 50px;
background-color: orange;
animation: enemies_move 4s alternate infinite;
}
.barriers {
width: 800px;
height: 20px;
position: fixed;
top: 460px;
display: flex;
justify-content: space-evenly;
}
.barriers div {
width: 100px;
height: 20px;
background-color: white;
}
.space_ship {
width: 50px;
height: 50px;
border: 2px white solid;
position: relative;
top: 440px;
left: 375px;
}
@keyframes shoot_bullet {
from {
top: 340px;
}
to {
top: -50px;
}
}
.div_bullet {
left: 17px;
width: 15px;
height: 40px;
background-color: white;
position: relative;
animation: shoot_bullet 1s;
}
<div id="main" >
<div >
<div id="e1"></div>
<div id="e2"></div>
<div id="e3"></div>
<div id="e4"></div>
<div id="e5"></div>
</div>
<div >
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div id="space_ship" ></div>
</div>
CodePudding user response:
The problem in your code is that you are using only the x
and y
of the bullet and enemy to check for collisions. You need to use the width and height of both items to check if they collide.
The checks needed to see if two rects collide is
function rectCollision(rectA, rectB) {
return (
rectA.x < rectB.x rectB.width &&
rectA.x rectA.width > rectB.x &&
rectA.y < rectB.y rectB.height &&
rectA.height rectA.y > rectB.y
);
}
And using this in your code makes it work
const shoot = (x) => {
//Creates bullet
const main = document.getElementById("main");
const div_bullet = document.createElement("div");
//Appends child
main.appendChild(div_bullet);
//Sets position
div_bullet.style.top = "340px";
div_bullet.style.left = x "px";
//Gives it a class
div_bullet.classList.add("div_bullet");
div_bullet.setAttribute("id", "bullet");
//Deletes bullet
setTimeout(() => {
div_bullet.remove();
}, 1000);
};
const load_game = () => {
//Adding movement to the spaceship
const space_ship = document.getElementById("space_ship");
let x = 375;
window.addEventListener("keydown", (e) => {
switch (e.key) {
case "ArrowLeft":
if (x <= 15) break;
x = x - 10;
space_ship.style.left = x "px";
break;
case "ArrowRight":
if (x >= 725) break;
x = x 10;
space_ship.style.left = x "px";
break;
case "ArrowUp":
if (document.getElementsByClassName("div_bullet").length === 0)
shoot(x 20);
break;
default:
break;
}
});
};
const collision_system = () => {
setInterval(() => {
//Checks if any enemy was hit
const e1 = document.getElementById("e1");
const e2 = document.getElementById("e2");
const e3 = document.getElementById("e3");
const e4 = document.getElementById("e4");
const e5 = document.getElementById("e5");
const enemies_position = [
e1.getBoundingClientRect(),
e2.getBoundingClientRect(),
e3.getBoundingClientRect(),
e4.getBoundingClientRect(),
e5.getBoundingClientRect(),
];
if (document.getElementById("bullet")) {
const bullet = document
.getElementById("bullet")
.getBoundingClientRect();
console.log(
"X: " enemies_position[0].x "Y: " enemies_position[0].y
);
console.log("X bullet: " bullet.x "Y bullet : " bullet.y);
for (let i = 0; i < 5; i ) {
if (
rectCollision(enemies_position[i], bullet)
)
alert("Shoot!");
}
}
}, 10);
};
function rectCollision(rectA, rectB) {
return (rectA.x < rectB.x rectB.width &&
rectA.x rectA.width > rectB.x &&
rectA.y < rectB.y rectB.height &&
rectA.height rectA.y > rectB.y)
}
document.addEventListener("DOMContentLoaded", () => {
load_game();
collision_system();
});
* {
padding: 0px;
margin: 0px;
}
body {
background-color: black;
width: 100%;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.main {
width: 800px;
height: 500px;
border: 2px white solid;
border-radius: 10px;
}
@keyframes enemies_move {
from {
left: 80px;
}
to {
left: -80px;
}
}
.enemies {
margin-top: 20px;
position: fixed;
width: 800px;
height: 50px;
display: flex;
justify-content: space-evenly;
}
.enemies div {
position: relative;
width: 50px;
height: 50px;
background-color: orange;
animation: enemies_move 4s alternate infinite;
}
.barriers {
width: 800px;
height: 20px;
position: fixed;
top: 460px;
display: flex;
justify-content: space-evenly;
}
.barriers div {
width: 100px;
height: 20px;
background-color: white;
}
.space_ship {
width: 50px;
height: 50px;
border: 2px white solid;
position: relative;
top: 440px;
left: 375px;
}
@keyframes shoot_bullet {
from {
top: 340px;
}
to {
top: -50px;
}
}
.div_bullet {
left: 17px;
width: 15px;
height: 40px;
background-color: white;
position: relative;
animation: shoot_bullet 1s;
}
<div id="main" >
<div >
<div id="e1"></div>
<div id="e2"></div>
<div id="e3"></div>
<div id="e4"></div>
<div id="e5"></div>
</div>
<div >
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div id="space_ship" ></div>
</div>