【HTMLのみ】簡易シューティングゲームを作ってみた

## ゲームの概要
ここでは、非常に簡単なインベーダーゲームのような遊びができる、シューティングゲームの作り方をご紹介します。下記のゲーム画面のように、遊ぶことができます。

**ゲームのプレイは[コチラ](https://miuradonuts.com/kaki-wars/)から**

![スクリーンショット 2023-06-28 20.58.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416622/0a2af864-f8e3-4034-84a5-76783961311d.png)

#### 同梱物
- index.html（ゲームのメインプログラムです）
- 背景画像（ゲームの背景となる画像です）
- 敵オブジェクト（牡蠣の画像です）
- Playerオブジェクト（忍者の画像です）

#### 仕様
- 移動：左右の矢印ボタンで移動します（移動は左右のみ）
- 攻撃：スペースで弾を発射します（押しっぱなしで連続発射も可能）
- 得点：牡蠣1体撃破につき、＋10点
- 当たり判定：牡蠣との接触では、当たり判定のカウントなし
- ゲームオーバー：設定なし


#### ソースコードや同梱物は、Githubへ公開中です（下記参照）
https://github.com/Rekt-Order/Kaki-Wars

#### ソースコード（html）
````
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>KAKI-WARS</title>
  <style>
    body {
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
      margin: 0;
      background-color: #222;
      background-image: url("back.png");
      background-size: cover;
    }
    canvas {
      border: 1px solid #ffffff;
    }
  </style>
</head>
<body>
  <canvas id="gameCanvas" width="480" height="320"></canvas>
  <script>
    const canvas = document.getElementById('gameCanvas');
    const ctx = canvas.getContext('2d');
    const keys = {};


    document.addEventListener('keydown', (e) => {
      keys[e.code] = true;

      // 追加: スペースキーが押されたときに弾を発射
      if (e.code === 'Space') {
        player.bullets.push({
          x: player.x + player.size / 2 - 2,
          y: player.y - 10,
          size: 4,
          speed: 6
        });
      }
    });

    document.addEventListener('keyup', (e) => {
      keys[e.code] = false;
    });

    let player = {
      x: canvas.width / 2,
      y: canvas.height - 30 - 30, // 上に30px移動
      size: 60,
      speed: 4,
      bullets: [],
      ready: false
    };

    let enemies = [];
    let enemySpawnCounter = 0;
    let moveDirection = 1;
    let moveDownCounter = 0;
    let score = 0;

    let playerImage = new Image();
    playerImage.src = "忍者-ジャンプ4.png";
    playerImage.onload = function() {
      player.image = playerImage;
      player.ready = true;
    };

    let enemyImage = new Image();
    enemyImage.src = "kaki.png";
    enemyImage.onload = function() {
      for (const enemy of enemies) {
        enemy.image = enemyImage;
        enemy.ready = true;
      }
    };

    function drawEnemies() {
      for (const enemy of enemies) {
        if (enemy.ready) {
          ctx.drawImage(enemy.image, enemy.x, enemy.y, enemy.size, enemy.size);
        }
      }
    }

    function drawBullets() {
      ctx.fillStyle = 'green';
      for (const bullet of player.bullets) {
        ctx.fillRect(bullet.x, bullet.y, bullet.size, bullet.size);
      }
    }

    function update() {
      ctx.clearRect(0, 0, canvas.width, canvas.height);

      if (player.ready) {
        ctx.drawImage(player.image, player.x, player.y, player.size, player.size);
      }

      drawEnemies();
      drawBullets();

      if (keys['ArrowLeft']) {
        player.x -= player.speed;
      }
      if (keys['ArrowRight']) {
        player.x += player.speed;
      }

      for (const bullet of player.bullets) {
        bullet.y -= bullet.speed;
      }
      player.bullets = player.bullets.filter(bullet => bullet.y > 0);

      // 弾と敵の衝突判定を追加
      player.bullets.forEach((bullet) => {
        enemies.forEach((enemy) => {
          if (
            bullet.x < enemy.x + enemy.size &&
            bullet.x + bullet.size > enemy.x &&
            bullet.y < enemy.y + enemy.size &&
            bullet.y + bullet.size > enemy.y
          ) {
            bullet.toRemove = true;
            enemy.toRemove = true;
            score += 10;
          }
        });
      });

      player.bullets = player.bullets.filter((bullet) => !bullet.toRemove);
      enemies = enemies.filter((enemy) => !enemy.toRemove);

      enemySpawnCounter++;
      if (enemySpawnCounter >= 30) {
        let newEnemy = {
          x: Math.random() * (canvas.width - 20),
          y: 10,
          size: 40,
          speed: 2,
          ready: false
        };
        newEnemy.image = enemyImage;
        newEnemy.ready = true;
        enemies.push(newEnemy);
        enemySpawnCounter = 0;
      }

      moveDownCounter++;
      if (moveDownCounter >= 60) {
        moveDirection = -moveDirection;
        for (const enemy of enemies) {
          enemy.y += 20;
        }
        moveDownCounter = 0;
      }

      for (const enemy of enemies) {
        enemy.x += enemy.speed * moveDirection;
      }
      enemies = enemies.filter(enemy => enemy.y < canvas.height);

      ctx.fillStyle = "white";
      ctx.font = "16px Arial";
      ctx.fillText("Score: " + score, 10, 20);

      requestAnimationFrame(update);
    }

    canvas.addEventListener('click', (e) => {
      player.bullets.push({
        x: player.x + player.size / 2 - 2,
        y: player.y - 10,
        size: 4,
        speed: 6
      });
    });

    update();
  </script>
</body>
</html>
````


## ゲームパッケージ

最後に…「KAKI WARS」ということで、あの懐かしいゲームの外箱っぽいデザインに仕上げてみました。もちろん、パッケージは存在しないので、架空の外箱となります。

![NINJA 牡蠣.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416622/4cb18eba-30ce-66ac-7a5e-72e61d3b6a8a.png)

この記事は、著者が運営するメディア「ぬるぺでぃあ」でも読むことができます（記事は[コチラ](https://miuradonuts.com/2023/06/28/note-002/)）
![](https://miuradonuts.com/wp-content/uploads/2023/06/ヘッダーエリア画像作成.png)