JavaScriptでアプリを作成しました【7】【Space Invaders】


はじめに

学習するに至った経緯

2020年より、未経験からエンジニアへの転職を目指し、某プログラミングスクールへ通う。入学後、『Ruby』を未経験から学ぶ人が多いのと『Ruby』の求人が思っていた以上に少ないので、卒業後、フロントエンドのエンジニアを目指す事に。
Javascriptの学習した事を言語化し、認識の深化による備忘録として記載。

index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">

    <title>Space Invaders</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <h1 class="results">0</h1>
    <div class="grid"></div>
    <script src="main.js"></script>

</body>
</html>
styles.css
.grid {
    width: 300px;
    height: 300px;
    border: solid black 1px;
    display: flex;
    flex-wrap: wrap;
}
.grid div{
    width: 20px;
    height: 20px;
}

.invader {
    background-color: purple;
    border-radius: 10px;
}

.shooter {
    background-color: green;
}

.laser {
    background-color: orange;
}

.boom {
    background-color: red;
}
main.js
//gridを取得
const grid = document.querySelector(".grid");
const resultsDisplay = document.querySelector(".results");

let currentShooterIndex = 202;
let width = 15;
let direction = 1;
let invadersId;
let goingRight = true;
let aliensRemoved = [];
let results = 0;

//forループを使用して正方形を作成
for (let i = 0; i < 225; i++) {
  //forループを実行し、ループするたび正方形を作成する
  const square = document.createElement("div");
  grid.appendChild(square);
}
//全ての正方形を取得
const squares = Array.from(document.querySelectorAll(".grid div"));
//エイリアンを取得 配列を使用
const alienInvaders = [
  0,1,2,3,4,5,6,7,8,9,
  15,16,17,18,19,20,21,
  22,23,24,30,31,32,33,
  34,35,36,37,38,39,
];
//インヴェーダーを正方形の中に配置する
function draw() {
  for (let i = 0; i < alienInvaders.length; i++) {
    if (!aliensRemoved.includes(i)) {
      squares[alienInvaders[i]].classList.add("invader");
    }
  }
}

draw();
//削除したインベーダーを作成する
function remove() {
  for (let i = 0; i < alienInvaders.length; i++) {
    squares[alienInvaders[i]].classList.remove("invader");
  }
}

squares[currentShooterIndex].classList.add("shooter");

//シューターを動かす
function moveShooter(e) {
  squares[currentShooterIndex].classList.remove("shooter");
  //キーボタンを押したら左右に1マスずつ移動する
  switch (e.key) {
    case "ArrowLeft":
      if (currentShooterIndex % width !== 0) currentShooterIndex -= 1;
      break;
    case "ArrowRight":
      if (currentShooterIndex % width < width - 1) currentShooterIndex += 1;
      break;
  }
  squares[currentShooterIndex].classList.add("shooter");
}
document.addEventListener("keydown", moveShooter);

//インベーダーが移動する
function moveInvaders() {
  const leftEdge = alienInvaders[0] % width === 0;
  const rightEdge = alienInvaders[alienInvaders.length - 1] % width === width - 1;
  remove();
  //最後のインベーダーが右端にいたら一段下げる
  if (rightEdge && goingRight) {
    //iを0にするためには、各インベーダーをループさせる。
    for (let i = 0; i < alienInvaders.length; i++) {
      alienInvaders[i] += width + 1;
      direction = -1;
      goingRight = false;
    }
  }
  //最後のインベーダーが左端にいたら一段下げる
  if (leftEdge && !goingRight) {
    for (let i = 0; i < alienInvaders.length; i++) {
      alienInvaders[i] += width - 1;
      direction = 1;
      goingRight = true;
    }
  }

  for (let i = 0; i < alienInvaders.length; i++) {
    alienInvaders[i] += direction;
  }
  draw();
  //シューターがインベーダーに当たったら、GAME OVER
  if (squares[currentShooterIndex].classList.contains("invader", "shooter")) {
    resultsDisplay.innerHTML = "GAME OVER";
    clearInterval(invadersId);
  }
  //インべーダーが底に当たったら、GAME OVER
  for (let i = 0; i < alienInvaders.length; i++) {
    if (alienInvaders[i] > squares.length + 100) {
      //   console.log("squares.length", squares.length);
      resultsDisplay.innerHTML = "GAME OVER";
      clearInterval(invadersId);
    }
  }
  if (aliensRemoved.length === alienInvaders.length) {
    resultsDisplay.innerHTML = "YOU WIN! WIN!";
    clearInterval(invadersId);
  }
}
invadersId = setInterval(moveInvaders, 300);

//インベーダーを射撃する
function shoot(e) {
  let laserId;
  let currentLaserIndex = currentShooterIndex;
  function moveLaser() {
    squares[currentLaserIndex].classList.remove("laser");
    currentLaserIndex -= width;
    squares[currentLaserIndex].classList.add("laser");

    //インベーダーにレーザーが当たると削除される
    if (squares[currentLaserIndex].classList.contains("invader")) {
      squares[currentLaserIndex].classList.remove("laser");
      squares[currentLaserIndex].classList.remove("invader");
      squares[currentLaserIndex].classList.add("boom");

      //レーザーによる爆発が起きたら、300ミリ秒後に削除される
      setTimeout(() => squares[currentLaserIndex].classList.remove("boom"), 300);
      clearInterval(laserId);
      //インベーダーにレーザーが当たっても再描画されているので、レーザーが当たると削除される
      const alienRemoved = alienInvaders.indexOf(currentLaserIndex);
      aliensRemoved.push(alienRemoved);
      //スコアを追加
      results++;
      //結果を表示する
      resultsDisplay.innerHTML = results;
      console.log(aliensRemoved);
    }
  }
  //キーの上を押すと射撃する
  switch (e.key) {
    case "ArrowUp":
      laserId = setInterval(moveLaser, 100);
  }
}
document.addEventListener("keydown", shoot);

参考サイト

Space Invaders in JavaScript (no-nonsense version!)