【JavaScript】2人プレイのディフェンスゲームを作ってみた

# はじめに
JavaScriptでcanvasを使って、2人プレイのディフェンスゲームを作ってみました。

プレイはこちらから→[ディフェンスゲーム](https://michimichix521.github.io/2PlayerDefenseGame/)

# 環境

- Windows 10 home
- Google Chrome

# ルール
2人プレイのゲームになります。
黄色の玉が画面右から左へ移動します。
画面左まで移動したときに、HPが減っていき、HPが0になるとゲーム終了です。
プレイヤー1と2は黄色の玉が画面の左へ移動するのを阻止する必要があります。
プレイヤー1は赤色の玉、プレイヤー2は青色の玉をそれぞれ操作し、黄色の玉に当たることで、黄色の玉を消すことができます。
このようにして、黄色の玉の移動を阻止していきましょう！
また、それぞれ黄色の玉に当たると得点も付くようにしているので、協力し合うことも、対戦で遊ぶこともできます。

# 操作方法

|1P|矢印キーでの移動|
|:-:|:-:|
|↑|上に移動|
|↓|下に移動|
|→|右に移動|
|←|左に移動|

|2P|WASDキーでの移動|
|:-:|:-:|
|W|上に移動|
|S|下に移動|
|A|右に移動|
|D|左に移動|

# プログラム

```index.html
<style>body{margin:0;}</style>
<canvas class="canvas">
<script>
"use strict";
{
const WIDTH = 1200; //キャンバスの横軸
const HEIGHT = 600; //キャンバスの縦軸
const HTML_CVS = document.querySelector(".canvas"); //キャンバスの領域の取得
const CANVAS = HTML_CVS.getContext("2d"); //キャンバスの描画機能を有効

class Circle{ //円クラス
  constructor(canvas,x,y,r,color){
    this.canvas = canvas;
    this.x = x;
    this.y = y;
    this.r = r;
    this.color = color;
  }

  draw(){ //描画
    this.canvas.beginPath(); //パスの初期化
    this.canvas.fillStyle = this.color;
    this.canvas.arc(this.x,this.y,this.r,0*Math.PI,2*Math.PI,true);
    this.canvas.closePath(); //パスを閉じる
    this.canvas.fill();
  }
}

class Player extends Circle{ //プレイヤークラス
  constructor(canvas,x,y,r,color){
    super(canvas,x,y,r,color);

    this.speed = 20; //プレイヤーの移動の速さ
    this.score = 0; //プレイヤーの得点

    this.up = false;
    this.down = false;
    this.right = false;
    this.left = false;
  }

  move(){ //キー操作での動き
    if(this.up && this.y-(this.r+this.speed)>=0){this.y -= this.speed;}
    if(this.down && this.y+(this.r+this.speed)<=HEIGHT){this.y += this.speed;}
    if(this.right && this.x+(this.r+this.speed)<=WIDTH){this.x += this.speed;}
    if(this.left && this.x-(this.r+this.speed)>=0){this.x -= this.speed;}
  }

  toEnemyDistance(enemy){ //敵との距離を算出
    return Math.sqrt((enemy.x-this.x)**2+(enemy.y-this.y)**2);
  }
}

class Enemy extends Circle{ //敵クラス
  constructor(canvas,x,y,r,color){
    super(canvas,x,y,r,color);
    this.y = Math.floor(Math.random()*(HEIGHT-this.r*2))+this.r;
    this.speed = 12;
  }

  move(){ //右から左への動き
    if(this.x > 0+this.r){
      this.x -= this.speed;
    }
  }
}

class ScoreLabel{
  constructor(canvas){
    this.canvas = canvas;
    this.x = 10;
    this.y = 40;
    this.hp = 20;
  }

  draw(p1,p2){
    this.canvas.fillStyle = "white";
    this.canvas.font = "30px Arial";
    this.canvas.fillText("hp : "+this.hp +" / P1 : "+p1.score+" / P2 : "+p2.score,this.x,this.y);
  }
}

class Game{
  constructor(){
    HTML_CVS.width = WIDTH;
    HTML_CVS.height = HEIGHT;

    this.PlayerRadius = 60; //プレイヤーの玉の半径
    this.EnemyRadius = 10; //敵の玉の半径
    this.frameRate = 50; //フレーム数
    this.timeCounter = 0; //タイムカウンタ
    this.intervalTime = 0.2; //敵の生成間隔時間
    this.gameflag = true;

    this.player1 = new Player(CANVAS,this.PlayerRadius,this.PlayerRadius,this.PlayerRadius,"red"); //1P
    this.player2 = new Player(CANVAS,this.PlayerRadius,HEIGHT-this.PlayerRadius,this.PlayerRadius,"blue"); //2P
    this.enemy = [];
    this.scoreLabel = new ScoreLabel(CANVAS);

    window.setInterval(()=>{ //ループ処理(フレーム数はFRAME_RATE)
      CANVAS.fillStyle = "black";
      CANVAS.fillRect(0,0,WIDTH,HEIGHT) //キャンバスを描画

      if(this.gameflag){
        this.player1.draw(); //プレイヤー1の描画
        this.player1.move(); //プレイヤー1の動き

        this.player2.draw(); //プレイヤー2の描画
        this.player2.move(); //プレイヤー2動き


        for(let i=0;i<this.enemy.length;i++){
          this.enemy[i].draw(); //敵の描画
          this.enemy[i].move(); //敵の動き
          if(this.enemy[i].x <= 0+this.enemy[i].r){
            this.enemy.splice(i,1);
            this.scoreLabel.hp--;
          }
          if(this.player1.toEnemyDistance(this.enemy[i])<=this.player1.r+this.enemy[i].r){ //距離による当たり判定
            this.enemy.splice(i,1);
            this.player1.score++;
            continue;
          }
          if(this.player2.toEnemyDistance(this.enemy[i])<=this.player2.r+this.enemy[i].r){ //距離による当たり判定
            this.enemy.splice(i,1);
            this.player2.score++;
            continue;
          }
        }
        this.timeCounter++;

        if(this.timeCounter % (this.frameRate*this.intervalTime) == 0){
          this.enemy.push(new Enemy(CANVAS,WIDTH-this.EnemyRadius,HEIGHT/2,this.EnemyRadius,"yellow"));
        }

        this.scoreLabel.draw(this.player1,this.player2);
        if(this.scoreLabel.hp <= 0){this.gameflag=false;}
        }else{alert("ゲームオーバー！");} //アラートでゲームオーバーを表示

    },1000/this.frameRate);

    window.addEventListener("keydown",()=>{ //キーボードのキーを押したときに処理
      if(event.keyCode==38){this.player1.up=true;} //上矢印キー
      if(event.keyCode==40){this.player1.down=true;} //下矢印キー
      if(event.keyCode==39){this.player1.right=true;} //右矢印キー
      if(event.keyCode==37){this.player1.left=true;} //左矢印キー

      if(event.keyCode==87){this.player2.up=true;} //Wキー
      if(event.keyCode==83){this.player2.down=true;} //Sキー
      if(event.keyCode==68){this.player2.right=true;} //Dキー
      if(event.keyCode==65){this.player2.left=true;} //A印キー
    });

    window.addEventListener("keyup",()=>{ //キーボードのキーを離したときに処理
      if(event.keyCode==38){this.player1.up=false;} //上矢印キー
      if(event.keyCode==40){this.player1.down=false;} //下矢印キー
      if(event.keyCode==39){this.player1.right=false;} //右矢印キー
      if(event.keyCode==37){this.player1.left=false;} //左矢印キー

      if(event.keyCode==87){this.player2.up=false;} //Wキー
      if(event.keyCode==83){this.player2.down=false;} //Sキー
      if(event.keyCode==68){this.player2.right=false;} //Dキー
      if(event.keyCode==65){this.player2.left=false;} //Aキー
    });
  }
}

new Game(); //ゲーム開始
}
</script>
```

以上で終了です！
ここまで読んでいただき、ありがとうございました。