JavaScriptでフルスクラッチゲーム開発しよう 第4回 更新編

第1回: [JavaScriptでフルスクラッチゲーム開発しよう 第1回 準備編](http://qiita.com/zukkun/items/4ea9542a6dde45c6a9fd)
第2回: [JavaScriptでフルスクラッチゲーム開発しよう 第2回 画像読み込み編](http://qiita.com/zukkun/items/f0348a8cc14090f184b8)
第3回: [JavaScriptでフルスクラッチゲーム開発しよう 第3回 画像表示編](http://qiita.com/zukkun/items/2b811701b32836c52415)

前回は、背景とみかん箱を表示するところまで出来ました。

今回は、みかん箱を動かしてみます。

## みかん箱の表示座標を管理する

みかん箱をX軸座標で動かすことを想定して、それを管理する変数を用意します。

```js:main.js
 :
// みかん箱の表示X軸座標位置
var mikanX = 0;
 :
function render() {
   :

  // みかん箱を表示
  ctx.drawImage(Asset.images['box'], mikanX, 0);
}
```

これで`mikanX`の値を変えることで、みかん箱のX軸座標の表示位置を変えることができました。

では、どのタイミングでどのように値を変化させるか、という部分に進みます。

## みかん箱を右に移動させていく

毎フレーム呼ばれる関数として`update`という関数を用意していました。

ここで`mikanX`の値を増加させて、右へ移動させていきます。

Canvasの座標系は「スクリーン座標系」で、XとY共に右下に行くほど値が増加します。

また、`drawImage`に指定する座標はピクセルという単位になります。

```js:main.js
function update() {
   :

  // 毎フレーム1pxずつ
  mikanX += 1;

   :
}
```

これを実行すると、みかん箱がじりじりと右へ移動していく様子を確認することができます。

## updateが呼ばれる頻度

`requestAnimationFrame`によって関数が呼び出される頻度は、Webブラウザに依存します。

描画頻度については「fps」という単位で呼ぶことがあります。

参考：[フレームレート - Wikipedia](http://ja.wikipedia.org/wiki/%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AC%E3%83%BC%E3%83%88)

大抵の環境では、60fpsで呼ばれますが、それでも、1フレームで行われた処理に時間がかかったり、またはWebブラウザ自体の挙動で処理が遅くなるなどの要因で、呼ばれるタイミングが60fpsより遅くなることがあります。

もし、上記のような何かが移動する処理において、1フレームで移動する距離を固定(上記の例ですと`1`で固定)している場合、もし描画タイミングが遅れた場合は移動速度が遅くなります。

ゲームのプレイ感が損なわれないためには、fpsが低下しても、同じ時間で同じ距離を移動しなければいけません。

## フレーム依存から時間依存へ

１フレームあたりの移動距離を固定するのではなく、前回フレームからの経過時間に応じて移動距離を計算する方式にします。

そうすることにより、`update`が呼ばれるタイミングが遅れたとしても、その分経過した時間を考慮して移動距離を計算することで、計算上の移動速度を固定にすることができます。

前回フレームからの経過時間を計算する方法はいくつかあるのですが、`requestAnimationFrame`に与えるコールバック関数の第1引数として高精度タイムスタンプの数値を利用した方法を使ってみます。

参考(英語)：[Timing control for script-based animations](http://www.w3.org/TR/animation-timing/#requestAnimationFrame)
参考(英語)：[DOMHighResTimeStamp - Web API インターフェイス | MDN](https://developer.mozilla.org/ja/docs/Web/API/DOMHighResTimeStamp)

まず`update`関数の第1引数として`timestamp`を用意します。ここの引数名は任意です。

```js:main.js
function update(timestamp) {
   :
}
```

この`timestamp`には、ミリ秒単位での経過時間が与えられます。（Chromeの環境では、ページを開いてから経過した時間、もしくは最初に`requestAnimationFrame`が呼ばれてからの経過した時間と思われる値になっていますが、厳密な仕様の情報は探せませんでした。すみません）

この`timestamp`を利用して「前回のフレームからの経過時間」を計算します。

まずは、前回フレームの`timestamp`を保持しておく必要があります。

```js:main.js
var lastTimestamp = null;
```

`lastTimestamp`が`null`の時は一番最初のフレームとみなし、経過時間は計測できないので、経過時間を0と固定にしておきます。

また、前回フレームからの経過時間の計算結果を、`update`関数の中で「秒」として`delta`という変数に入れておきます。

経過時間の変数名も任意ですが、deltaという命名は、数学で言う「変化あたりの増加量」のときに用いられる言葉で、1フレームという変化あたりの時間の変化量という意味で用いています。わかりにくい場合は「elapsedTime」という直訳の言葉でも良いでしょう。

計算結果を「秒」にするのは、ある値の変化速度について、あとあと「1秒あたりの変化量」で統一してわかりやすくするためです。例えば「1秒に100ピクセル移動する速度」を計算するときに`value += 100 * delta`という単純な式で表せられるように出来ます。

このへんの知識については「ゲーム開発、数学、物理学」あたりのキーワードで調べると学べますので、詳しい解説はそちらに任せます。

その前回フレームからの経過時間の計算を`update`関数に書いていきます。

```js:main.js
function update(timestamp) {
  var delta = 0; // 前回フレーム時間からの経過時間(単位:秒)
  if (lastTimestamp != null) {
    delta = (timestamp - lastTimestamp) / 1000;  // ミリ秒を1000で割ると秒になる(1000ミリ秒÷1000は1秒)
  }
  lastTimestamp = timestamp;

   :
}
```

これで`delta`を用いることで、前回フレームからの経過時間を使うことができます。

これで、さっそくみかん箱の移動速度を、経過時間にもとづいて計算してみましょう。

```js:main.js
function update(timestamp) {
   :

  // 秒速100px
  mikanX += 100 * delta;

   :  
}
```

「どうしてこの計算で秒速100ピクセルになるのか」を理解するためのイメージとしては、`delta`には「60分の1秒」の値が入るとして、`mikanX`に1回で足し込む量は「100×(60分の1)」つまり「100を60で分割した値」になり、それを60回繰り返す(60fpsは1秒に60フレームという意味)、つまり「60分の1秒が60回」なので1秒で合計100足し込まれる、というものです。言葉にするとわかりにくい……。

これで実行してみると、1秒あたり100ピクセルの速度でみかん箱が右に移動する様子を確認することができます。

![move_mikanbox.gif](https://qiita-image-store.s3.amazonaws.com/0/636/a2ed983d-07ff-3307-46e1-3e2487b5f736.gif)

これで、ゲームの色々な時間による変化を、`delta`を使って計算することができるようになりました。