第1回: JavaScriptでフルスクラッチゲーム開発しよう 第1回 準備編
第2回: JavaScriptでフルスクラッチゲーム開発しよう 第2回 画像読み込み編
第3回: JavaScriptでフルスクラッチゲーム開発しよう 第3回 画像表示編
前回は、背景とみかん箱を表示するところまで出来ました。
今回は、みかん箱を動かしてみます。
みかん箱の表示座標を管理する
みかん箱をX軸座標で動かすことを想定して、それを管理する変数を用意します。
:
// みかん箱の表示X軸座標位置
var mikanX = 0;
:
function render() {
:
// みかん箱を表示
ctx.drawImage(Asset.images['box'], mikanX, 0);
}
これでmikanX
の値を変えることで、みかん箱のX軸座標の表示位置を変えることができました。
では、どのタイミングでどのように値を変化させるか、という部分に進みます。
みかん箱を右に移動させていく
毎フレーム呼ばれる関数としてupdate
という関数を用意していました。
ここでmikanX
の値を増加させて、右へ移動させていきます。
Canvasの座標系は「スクリーン座標系」で、XとY共に右下に行くほど値が増加します。
また、drawImage
に指定する座標はピクセルという単位になります。
function update() {
:
// 毎フレーム1pxずつ
mikanX += 1;
:
}
これを実行すると、みかん箱がじりじりと右へ移動していく様子を確認することができます。
updateが呼ばれる頻度
requestAnimationFrame
によって関数が呼び出される頻度は、Webブラウザに依存します。
描画頻度については「fps」という単位で呼ぶことがあります。
大抵の環境では、60fpsで呼ばれますが、それでも、1フレームで行われた処理に時間がかかったり、またはWebブラウザ自体の挙動で処理が遅くなるなどの要因で、呼ばれるタイミングが60fpsより遅くなることがあります。
もし、上記のような何かが移動する処理において、1フレームで移動する距離を固定(上記の例ですと1
で固定)している場合、もし描画タイミングが遅れた場合は移動速度が遅くなります。
ゲームのプレイ感が損なわれないためには、fpsが低下しても、同じ時間で同じ距離を移動しなければいけません。
フレーム依存から時間依存へ
1フレームあたりの移動距離を固定するのではなく、前回フレームからの経過時間に応じて移動距離を計算する方式にします。
そうすることにより、update
が呼ばれるタイミングが遅れたとしても、その分経過した時間を考慮して移動距離を計算することで、計算上の移動速度を固定にすることができます。
前回フレームからの経過時間を計算する方法はいくつかあるのですが、requestAnimationFrame
に与えるコールバック関数の第1引数として高精度タイムスタンプの数値を利用した方法を使ってみます。
参考(英語):Timing control for script-based animations
参考(英語):DOMHighResTimeStamp - Web API インターフェイス | MDN
まずupdate
関数の第1引数としてtimestamp
を用意します。ここの引数名は任意です。
function update(timestamp) {
:
}
このtimestamp
には、ミリ秒単位での経過時間が与えられます。(Chromeの環境では、ページを開いてから経過した時間、もしくは最初にrequestAnimationFrame
が呼ばれてからの経過した時間と思われる値になっていますが、厳密な仕様の情報は探せませんでした。すみません)
このtimestamp
を利用して「前回のフレームからの経過時間」を計算します。
まずは、前回フレームのtimestamp
を保持しておく必要があります。
var lastTimestamp = null;
lastTimestamp
がnull
の時は一番最初のフレームとみなし、経過時間は計測できないので、経過時間を0と固定にしておきます。
また、前回フレームからの経過時間の計算結果を、update
関数の中で「秒」としてdelta
という変数に入れておきます。
経過時間の変数名も任意ですが、deltaという命名は、数学で言う「変化あたりの増加量」のときに用いられる言葉で、1フレームという変化あたりの時間の変化量という意味で用いています。わかりにくい場合は「elapsedTime」という直訳の言葉でも良いでしょう。
計算結果を「秒」にするのは、ある値の変化速度について、あとあと「1秒あたりの変化量」で統一してわかりやすくするためです。例えば「1秒に100ピクセル移動する速度」を計算するときにvalue += 100 * delta
という単純な式で表せられるように出来ます。
このへんの知識については「ゲーム開発、数学、物理学」あたりのキーワードで調べると学べますので、詳しい解説はそちらに任せます。
その前回フレームからの経過時間の計算をupdate
関数に書いていきます。
function update(timestamp) {
var delta = 0; // 前回フレーム時間からの経過時間(単位:秒)
if (lastTimestamp != null) {
delta = (timestamp - lastTimestamp) / 1000; // ミリ秒を1000で割ると秒になる(1000ミリ秒÷1000は1秒)
}
lastTimestamp = timestamp;
:
}
これでdelta
を用いることで、前回フレームからの経過時間を使うことができます。
これで、さっそくみかん箱の移動速度を、経過時間にもとづいて計算してみましょう。
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ピクセルの速度でみかん箱が右に移動する様子を確認することができます。
これで、ゲームの色々な時間による変化を、delta
を使って計算することができるようになりました。