[連載]スーパーマリオ的なゲームをjavascriptで作ってみる 初級編 〜3章〜 画像を動かしてみる

## 本連載について
- プログラミング初心者がスーパーマリオ的なゲームを作成するのに情報をまとめたものです
- 不明点や不備あれば、ばしばしコメントいただきたいです！！！より良いものにしたいので！
- 一番最初の連載は[こちら](https://qiita.com/hockeyarchitecture/items/139c27ff4806eaf2367f)から確認お願いします！<br/>
▼ゲームイメージ<br/>
![ゲームイメージ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/499206/949bb1ad-7109-1c70-b116-36ef83df3aa9.gif)<br/>
<br/>
▼目次<br/>
[1章 準備する](https://qiita.com/hockeyarchitecture/items/139c27ff4806eaf2367f)<br/>
[2章 簡単なページ作ってみる](https://qiita.com/hockeyarchitecture/items/76e0e90c1883d91b3e87)<br/>
[3章 画像を動かしてみる](https://qiita.com/hockeyarchitecture/items/c526e2345cb512109e18)<br/>
[4章 落ちちゃダメ！！](https://qiita.com/hockeyarchitecture/items/bea44d14aa16aa7f916e)<br/>



## 本章の概要
- 本章では主人公の画像を表示して、キーボードの入力に応じて動かすとこまでいきます
- ゲームっぽくジャンプしたり、落ちたり敵にぶつかってゲームオーバーになったりはこの後やります
- 本章の内容は大きく4ステップです
  - その１ 〜画像を表示できるよの巻〜
  - その２ 〜画像が動くよの巻〜
  - その３ 〜キーボードの入力が検知できるよの巻〜
  - その４ 〜画像を動かせるよの巻〜
- 各ステップごとに実際のソースをQiita上に記載しています<br/>
- 上記と同じくソースの実態を保存しているgitのリポジトリも記載しています<br/>
リンクにアクセスして実際のソースをダウンロードすることができます<br/>
**ぜひダウンロードして動かしながら試してみてください！**


## その１ 〜画像を表示できるよの巻〜
### ゴール
- 特定の画像をcanvas要素のエリアに表示すること
- マリオのゲーム作るにはマリオを画面に表示することは必要ですからね！

### 前提
- 大枠の流れとしては、 `canvas` 要素に対して `どこに` `どんな画像` を表示するかを指定します
- `canvas` 要素に対して画像の指定する際は `drawImage` の関数を使用します
- 画像を表示する場合、主に `画像ファイルのパス指定する` または `画像をエンコードして文字列としたものを記載する` の2択がありますが今回は前者を採用します

### やること
- 画像ファイルを作成し、配置します ([[参考]ドッド絵を作成する](http://neutralx0.net/tool/dot2/))
- `index.js` で `index.html` で定義した `canvas` 要素を取得するように修正します
- `Image` 要素を作成し、 `canvas` 要素に対して追加(draw)するように修正します
- フォルダ階層は以下ようにしています<br/>

```
└┬─ src    ┬─ index.html
 │         └─ index.js
 └─ images ── character-01 ── base.png
```

▼ソース

```javascript:index.js
// canvas要素の取得
const canvas = document.getElementById("maincanvas");
const ctx = canvas.getContext("2d");

// ロード時に画面描画の処理が実行されるようにする
window.addEventListener("load", update);

// 画面を更新する関数を定義 (繰り返しここの処理が実行される)
function update() {
  // 画面全体をクリア
  ctx.clearRect(0, 0, 640, 480);

  // 主人公の画像を表示
  var image = new Image();
  image.src = "../images/character-01/base.png";
  ctx.drawImage(image, 0, 0, 32, 32);

  // 再描画
  window.requestAnimationFrame(update);
}
```

※ 実際のソースコードは [こちら](https://github.com/taku7777777/mario-game-tutorial-01-03-01) からダウンロードできます

▼CodePenのサンプル

<p class="codepen" data-height="300" data-theme-id="default" data-default-tab="js,result" data-user="taku7777777" data-slug-hash="yLyPQaP" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="mario-game-tutorial-01-03-01">
  <span>See the Pen <a href="https://codepen.io/taku7777777/pen/yLyPQaP">
  mario-game-tutorial-01-03-01</a> by taku7777777 (<a href="https://codepen.io/taku7777777">@taku7777777</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

### 説明
- `document.getElementById("maincanvas")` で `canvas` 要素を取得しています<br/>
`maincanvas` は `index.html` で `canvas` 要素の `id` として指定しているものです<br/>
`index.js` 内から `index.html` に記載している要素を取得することができます
- `canvas.getContext("2d")` で取得した `canvas` 要素からさらに二次元の情報(今回描画したい対象の情報)を取得します<br/>
ここまでは定型文のようなものなので、深く突っ込まず「へぇーこうゆうふうに記載すればいいんだ」くらいでOKです！
- `ctx.clearRect(0, 0, 640, 480)` で `canvas` 要素の描画対象をクリアしている<br/>
これをしないと、この後に実施する画像表示の処理が重複して、意図せず複数の画像が表示されかねないので注意です
- `var image = new Image()` で新しく画像要素を作成し、 `image.src = "../images/character-01/base.png"` で画像要素の画像情報を定義しています
- `ctx.drawImage(image, 0, 0, 32, 32)` では `どの画像要素` を `どこに` `どのような大きさで` 表示するかを定義しています<br/>
今回の例では、`"../images/character-01/base.png"` の画像をx座標が0(一番左から右に0ピクセル移動したとこ)、y座標が0(一番上から下に0ピクセル移動したとこ)に横幅32ピクセル、縦幅32ピクセルで画像を表示するということになります<br/>
例えば `ctx.drawImage(image, 100, 200, 10, 20)` の場合x座標が100(一番左から右に100ピクセル移動したとこ)、y座標が200(一番上から下に200ピクセル移動したとこ)に横幅10ピクセル、縦幅20ピクセルで画像を表示するということになります<br/>


## その２ 〜画像が動くよの巻〜
### ゴール
- 表示した画像が横に少しづつ動くようにします！(超簡単なアニメーション！？)

### やること
- 「その１ 〜画像を表示できるよの巻〜」で対応した `drawImage` の画像表示位置を少しづつ変えます
- 最初は一番左に画像を表示し、一定の速度で右に移動するようにします

▼ソース

```javascript:index.js
// canvas要素の取得
const canvas = document.getElementById("maincanvas");
const ctx = canvas.getContext("2d");

// ロード時に画面描画の処理が実行されるようにする
window.addEventListener("load", update);

// 主人公の初期座標を定義
var x = 0;

// 画面を更新する関数を定義 (繰り返しここの処理が実行される)
function update() {
  // 画面全体をクリア
  ctx.clearRect(0, 0, 640, 480);

  // 座標を更新する
  x = x + 1;

  // 主人公の画像を表示
  var image = new Image();
  image.src = "../images/character-01/base.png";
  ctx.drawImage(image, x, 0, 32, 32);

  // 再描画
  window.requestAnimationFrame(update);
}
```

※ 実際のソースコードは [こちら](https://github.com/taku7777777/mario-game-tutorial-01-03-02) からダウンロードできます

▼CodePenのサンプル (右下の"return"をクリックして、リフレッシュしてから確認してみてください)

<p class="codepen" data-height="300" data-theme-id="default" data-default-tab="js,result" data-user="taku7777777" data-slug-hash="rNaYQjK" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="mario-game-tutorial-01-03-02">
  <span>See the Pen <a href="https://codepen.io/taku7777777/pen/rNaYQjK">
  mario-game-tutorial-01-03-02</a> by taku7777777 (<a href="https://codepen.io/taku7777777">@taku7777777</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

### 説明
- x座標を変数化し(x)、update処理が実施されるごとに1増えるようにしています

## その３ 〜キーボードの入力が検知できるよの巻〜
### ゴール
- キーボードで上下右左が押されたことを検知できるようにします
- これができると入力に応じていろんなことをできるようになります！

### やること
- 以下のソースをコピペ<br/>
(これも定型文みたいなものなので細かいとこは気にしなくて大丈夫ですー)

```javascript:index.js
// キーボードの入力状態を記録する配列の定義
var input_key_buffer = new Array();

// キーボードの入力イベントをトリガーに配列のフラグ値を更新させる
window.addEventListener("keydown", handleKeydown);
function handleKeydown(e) {
  console.log("key down : " + e.keyCode);
  input_key_buffer[e.keyCode] = true;

  if (e.keyCode === 37) {
    alert("左が押されたよ");
  } else if (e.keyCode === 38) {
    alert("上が押されたよ");
  } else if (e.keyCode === 39) {
    alert("右が押されたよ");
  } else if (e.keyCode === 40) {
    alert("下が押されたよ");
  }
}

window.addEventListener("keyup", handleKeyup);
function handleKeyup(e) {
  console.log("key up : " + e.keyCode);
  input_key_buffer[e.keyCode] = false;

  if (e.keyCode === 37) {
    alert("左がはなされたよ");
  } else if (e.keyCode === 38) {
    alert("上がはなされたよ");
  } else if (e.keyCode === 39) {
    alert("右がはなされたよ");
  } else if (e.keyCode === 40) {
    alert("下がはなされたよ");
  }
}
```

※ 実際のソースコードは [こちら](https://github.com/taku7777777/mario-game-tutorial-01-03-03) からダウンロードできます

### 説明
- `window.addEventListener("keydown", XXXXX)` キーボードが押された際に `XXXXX` の関数を実行するよって定義することができます
- `window.addEventListener("keyup", XXXXX)` キーボードが押された状態からはなされた際に `XXXXX` の関数を実行するよって定義することができます
- `handleKeydown` `handleKeyup` でキーボーが押された際/はなされた際に実行される関数を定義しています<br/>
引数 `(e)` の部分でキーボードが押された/はなされたイベント情報をもらい、なんのキーが入力されたかを `e.keyCode` で取得することができます
- `alert`のとこの分岐は処理を確認するためのおまけ、本当は不要なコードになります

## その４ 〜画像を動かせるよの巻〜
### ゴール
- その２、その３を組み合わせて上下左右のボタンを押した方向に画像が動くようにします
- ちょっとゲームっぽくなってくるのではないでしょうか？

### やること
- 画像のx座標、y座標を変数化します
- キーボードの入力状態に応じてx座標、y座標を更新します

▼ソース

```javascript:index.js
// キーボードの入力状態を記録する配列の定義
var input_key_buffer = new Array();

// キーボードの入力イベントをトリガーに配列のフラグ値を更新させる
window.addEventListener("keydown", handleKeydown);
function handleKeydown(e) {
  console.log("key down : " + e.keyCode);
  input_key_buffer[e.keyCode] = true;
}

window.addEventListener("keyup", handleKeyup);
function handleKeyup(e) {
  console.log("key up : " + e.keyCode);
  input_key_buffer[e.keyCode] = false;
}

// canvas要素の取得
const canvas = document.getElementById("maincanvas");
const ctx = canvas.getContext("2d");

// 画像を表示するの座標の定義 & 初期化
var x = 0;
var y = 0;

// ロード時に画面描画の処理が実行されるようにする
window.addEventListener("load", update);

// 画面を更新する関数を定義 (繰り返しここの処理が実行される)
function update() {
  // 画面全体をクリア
  ctx.clearRect(0, 0, 640, 480);

  // 入力値の確認と反映
  if (input_key_buffer[37]) {
    // 左が押されていればx座標を1減らす
    x = x - 1;
  }
  if (input_key_buffer[38]) {
    // 上が押されていればy座標を1減らす
    y = y - 1;
  }
  if (input_key_buffer[39]) {
    // 右が押されていればx座標を1増やす
    x = x + 1;
  }
  if (input_key_buffer[40]) {
    // 下が押されていればy座標を1増やす
    y = y + 1;
  }

  // 主人公の画像を表示
  var image = new Image();
  image.src = "../images/character-01/base.png";
  ctx.drawImage(image, x, y, 32, 32);

  // 再描画
  window.requestAnimationFrame(update);
}
```

※ 実際のソースコードは [こちら](https://github.com/taku7777777/mario-game-tutorial-01-03-04) からダウンロードできます

▼CodePenのサンプル

<p class="codepen" data-height="300" data-theme-id="default" data-default-tab="js,result" data-user="taku7777777" data-slug-hash="dyPZQJb" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="mario-game-tutorial-01-03-04">
  <span>See the Pen <a href="https://codepen.io/taku7777777/pen/dyPZQJb">
  mario-game-tutorial-01-03-04</a> by taku7777777 (<a href="https://codepen.io/taku7777777">@taku7777777</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

### 説明
- `input_key_buffer` に `keyCode` ごとにおされているか(true)、押されていないか(false)を保持しているので、update処理を実行する際に押されているキーに応じて座標を更新します

## 終わりに
お疲れまです！！！<br/>
ゲームっぽさが出てきましたね！<br/>
次に進みましょう！！！<br/>
[４章へすすむ!!!](https://qiita.com/hockeyarchitecture/items/bea44d14aa16aa7f916e)
<br/><br/><br/>