Home > Mobile >  Detecting the collision of 2 divs with JavaScript
Detecting the collision of 2 divs with JavaScript

Time:06-13

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>

  • Related