ゲームを作りながら学ぶ！JavaScript レベルアップ講座 part5

こんにちは、[Yui](https://twitter.com/home)です。

今回は[前回](https://qiita.com/Yui_active/items/6e374b90bc873a5ba927)描画したスライムを動かす方法について書いていきます。
JavaScript道場は[こちら](https://x-hack.connpass.com/event/196255/?utm_campaign=event_publish_to_series_watcher&utm_source=notifications&utm_medium=email&utm_content=detail_btn)。

今回の成果物はこちらです。
<a href="https://gyazo.com/b24c48fd192956fbb78a0a0698e69f7b"><img src="https://i.gyazo.com/b24c48fd192956fbb78a0a0698e69f7b.gif" alt="Image from Gyazo" width="636"/></a>

キーボードの↑↓→←キーで動かしています。

# スライムのデフォルトの場所を中心にする

前回は(0,0)を始点にしてましたが、それだと少し見にくいのでスライムのデフォルトの位置を中央にしましょう。
絶対値で表しても良いのですが、今回はcanvasサイズから計算します。

```html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <canvas id="canvas" width="640" height="480" style="background-color: black;"></canvas>
  <script>
    const CANVAS_SIZE_W = 640
    const CANVAS_SIZE_HW = 320 // 画面の半分の広さ
    const CANVAS_SIZE_H = 480
    const CANVAS_SIZE_HH = 240 // 画面の半分の高さ
    const CHARACTER_SIZE = 100 // キャラクターの大きさ
    const CHARACTER_HSIZE = 50 // キャラクターの半分の大きさ
    let CHARACTER_POS_X = CANVAS_SIZE_HW - CHARACTER_HSIZE
    let CHARACTER_POS_Y = CANVAS_SIZE_HH - CHARACTER_HSIZE
    const canvas = document.getElementById("canvas")
    const ctx = canvas.getContext("2d")
    const img = new Image()
    img.src = "https://cache-www.dragonquest.jp/hoshidora/assets_190310/narikirislime/pc/slime.png"
    img.onload = () => ctx.drawImage(
      img,
      CANVAS_SIZE_HW - CHARACTER_HSIZE,
      CANVAS_SIZE_HH - CHARACTER_HSIZE,
      CHARACTER_SIZE,
      CHARACTER_SIZE
    )
  </script>
</body>
</html>
```

これでスライムが中央に来ました。

# キーボードに反応してスライムが動くようにする

そして今回肝となるキーボードに反応する部分です。
そのためには``addEventListener``を使います。

公式ドキュメントはこちら→https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener

今回は[window](https://developer.mozilla.org/ja/docs/Web/API/Window)が対象になりますので、``window.addEventListener``という風に書きます。

以下を追加します。

```javascript

    window.addEventListener("keydown", (e) => {
      if (e.key === "ArrowLeft") {
        CHARACTER_POS_X -= 10
        ctx.drawImage(
          img,
          CHARACTER_POS_X,
          CHARACTER_POS_Y,
          CHARACTER_SIZE,
          CHARACTER_SIZE
        )
      }
      if (e.key === "ArrowUp") {
        CHARACTER_POS_Y -= 10
        ctx.drawImage(
          img,
          CHARACTER_POS_X,
          CHARACTER_POS_Y,
          CHARACTER_SIZE,
          CHARACTER_SIZE
        )
      }
      if (e.key === "ArrowRight") {
        CHARACTER_POS_X += 10
        ctx.drawImage(
          img,
          CHARACTER_POS_X,
          CHARACTER_POS_Y,
          CHARACTER_SIZE,
          CHARACTER_SIZE
        )
      }
      if (e.key === "ArrowDown") {
        CHARACTER_POS_Y += 10
        ctx.drawImage(
          img,
          CHARACTER_POS_X,
          CHARACTER_POS_Y,
          CHARACTER_SIZE,
          CHARACTER_SIZE
        )
      }
    })
```

ここで、``keydown``というのはkeyがdownしたとき、つまりキーを押してる状態のことをいいます。
そのときにどのキーが押されたかによって反応を変えたいのでeという変数に入れてあげます。
もし→がArrowRightなどと覚えている場合は良いのですが、もし覚えていない場合はここで``console.log(e)``などをするとわかりやすいかと思います。

![スクリーンショット 2020-10-19 22.08.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670446/8932a754-fb43-e7cf-dfa0-ab35846f5313.png)

``console.log(e)``をした状態で→のキーを押してログを出した状態です。
これを見ると→をクリックすると、**e.key**が**ArrowRight**になることがわかります。これを利用して↑↓→←で動きをそれぞれ付けたのが上記のコードです。

この状態で動かすとこのようになります。

<a href="https://gyazo.com/e5d24c023abfb32f6751b539bb845948"><img src="https://i.gyazo.com/e5d24c023abfb32f6751b539bb845948.gif" alt="Image from Gyazo" width="634"/></a>

スライムが動くことは動くのですが、軌跡まで残ってしまいます。

# スライムが動くたびに毎回描画し直す

軌跡を消すために使うのがcanvasの中の``clearRect``というメソッドです。
これは範囲を指定することで、その範囲内のものをすべてリセットすることができます。

そういうわけで、``ctx.clearRect(0 ,0, CANVAS_SIZE_W, CANVAS_SIZE_H);``を以下のように追加します。

```javascript

    window.addEventListener("keydown", (e) => {
      ctx.clearRect(0 ,0, CANVAS_SIZE_W, CANVAS_SIZE_H);
      if (e.key === "ArrowLeft") {
        CHARACTER_POS_X -= 10
        ctx.drawImage(
          img,
          CHARACTER_POS_X,
          CHARACTER_POS_Y,
          CHARACTER_SIZE,
          CHARACTER_SIZE
        )
      }
      if (e.key === "ArrowUp") {
        CHARACTER_POS_Y -= 10
        ctx.drawImage(
          img,
          CHARACTER_POS_X,
          CHARACTER_POS_Y,
          CHARACTER_SIZE,
          CHARACTER_SIZE
        )
      }
      if (e.key === "ArrowRight") {
        CHARACTER_POS_X += 10
        ctx.drawImage(
          img,
          CHARACTER_POS_X,
          CHARACTER_POS_Y,
          CHARACTER_SIZE,
          CHARACTER_SIZE
        )
      }
      if (e.key === "ArrowDown") {
        CHARACTER_POS_Y += 10
        ctx.drawImage(
          img,
          CHARACTER_POS_X,
          CHARACTER_POS_Y,
          CHARACTER_SIZE,
          CHARACTER_SIZE
        )
      }
    })
```

これで軌跡が消え、いい感じにスライムが動くようになりました！

#クラス構文を使って書く

上記のままでももちろん良いのですが、今後スライムがたくさん出てきたときのために、クラス構文化して書いておきます。

```html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <canvas id="canvas" width="640" height="480" style="background-color: black;"></canvas>
  <script>
    const CANVAS_SIZE_W = 640
    const CANVAS_SIZE_HW = 320 // 画面の半分の広さ
    const CANVAS_SIZE_H = 480
    const CANVAS_SIZE_HH = 240 // 画面の半分の高さ
    const CHARACTER_SIZE = 100 // キャラクターの大きさ
    const CHARACTER_HSIZE = 50 // キャラクターの半分の大きさ

    const canvas = document.getElementById("canvas")
    const ctx = canvas.getContext("2d")
    class Slime {
      constructor(posX, posY) {
        const img = new Image()
        img.src = "https://cache-www.dragonquest.jp/hoshidora/assets_190310/narikirislime/pc/slime.png"
        this.img = img
        this.posX = posX
        this.posY = posY
        this.sizeX = 100
        this.sizeY = 100
        img.onload = () => this.drawImage()
      }
      drawImage() {
        ctx.drawImage(this.img, this.posX, this.posY, this.sizeX, this.sizeY)
      }
      move(e) {
        ctx.clearRect(0 ,0, CANVAS_SIZE_W, CANVAS_SIZE_H);
        if (e.key === "ArrowLeft") {
          this.posX -= 10
        }
        if (e.key === "ArrowUp") {
          this.posY -= 10
        }
        if (e.key === "ArrowRight") {
          this.posX += 10
        }
        if (e.key === "ArrowDown") {
          this.posY += 10
        }
        this.drawImage()
      }
    }
    let s = new Slime(CANVAS_SIZE_HW - CHARACTER_HSIZE, CANVAS_SIZE_HH - CHARACTER_HSIZE)
    window.addEventListener("keydown", (e) => {
      s.move(e)
    })
  </script>
</body>
</html>
```

こうすることで、今後スライムを追加したい場合は``new Slime``と書くだけで生成することができますね！

クラス構文、便利ですね。

今回はスライム一個だけの設計なので、正直クラス構文にしてもしなくてもどちらでも問題は無いです。
ただ、今後ゲームを作っていく中で、敵をいくつか生成したりすることがあるかとおもいます。
そのたびに毎回同じコードを書くようでは効率が悪いので、使えるときはクラス構文でまとめておくのが楽でいいですね。

それでは今回はスライムを動かすところまでやりました。

それでは次回は**パーティメンバーのステータスを表示する部分**を実装していきます。