LoginSignup
40
41

More than 5 years have passed since last update.

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

Posted at

第1回: JavaScriptでフルスクラッチゲーム開発しよう 第1回 準備編
第2回: JavaScriptでフルスクラッチゲーム開発しよう 第2回 画像読み込み編
第3回: JavaScriptでフルスクラッチゲーム開発しよう 第3回 画像表示編

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

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

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

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

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

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

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

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

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

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

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

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

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

main.js
function update() {
   :

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

   :
}

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

updateが呼ばれる頻度

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

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

参考:フレームレート - Wikipedia

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

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

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

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

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

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

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

参考(英語):Timing control for script-based animations
参考(英語):DOMHighResTimeStamp - Web API インターフェイス | MDN

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

main.js
function update(timestamp) {
   :
}

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

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

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

main.js
var lastTimestamp = null;

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

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

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

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

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

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

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

   :
}

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

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

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

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

40
41
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
40
41