ゲーム開発ではシューティングやベルトスクロールアクションなど、背景画像を延々とスクロール表示させるとがある。それをJavaScriptで実装してみた。キャンバスサイズは幅600、高さ400。
画像素材:bevouliin.com
まず、画像を表示する簡単なViewImageクラスを用意。
/**
* 画像を表示(viewimage.js)
*/
export class ViewImage {
constructor(parent = undefined, x = 0, y = 0, url = "") {
if (parent !== undefined) {
this._parent = parent;
}
this._x = x;
this._y = y;
this._width = 100;
this._height = 100;
this._alpha = 1.0;
this._img;
this._imgUrl = url;
this._init();
}
//////////////////////////////////
// Private and protected
//////////////////////////////////
_init() {
if (this._parent !== undefined) {
this._ctx = this._parent;
}
if (this._imgUrl !== "") {
this._img = new Image();
this._img.src = this._imgUrl;
this.draw();
}
}
//////////////////////////////////
// Public
//////////////////////////////////
draw() {
// 現在の描画状態を保存
this._ctx.save();
// コンテキストの座標を変更し、キャンバス中央に移動
this._ctx.translate(this._x, this._y);
this._ctx.globalAlpha = this._alpha;
this._ctx.drawImage(this._img, 0, 0, this._width, this._height);
// save()で保存した描画状態を復元
this._ctx.restore();
}
//////////////////////////////////
// Getters/Setters
//////////////////////////////////
get x() {
return this._x;
}
set x(x) {
this._x = x;
}
get y() {
return this._y;
}
set y(y) {
this._y = y;
}
get width() {
return this._width;
}
set width(width) {
this._width = width;
}
get height() {
return this._height;
}
set height(height) {
this._height = height;
}
get alpha() {
return this._alpha;
}
set alpha(alpha) {
this._alpha = alpha;
}
get url() {
return this._imgUrl;
}
set url(url) {
this._imgUrl = url;
}
}
次に、画像を繰り返しスクロール表示するBgScrollクラス。
import { ViewImage } from "./viewimage.js"; /** * 背景のスクロール処理(bgscroll.js) */ export class BgScroll { constructor() { this._cvs = document.getElementById('canvas'); this._ctx = this._cvs.getContext('2d'); this._bgImg1; this._bgImg2; this._vx = 1; // タイマー関連 this._animID; this._isAnim = 0; this._FPS = 60; this._frame = 0; this._startTime; this._nowTime; this._init(); } ////////////////////////////////// // Private and protected ////////////////////////////////// _init() { this._cvs.style.backgroundColor = "#eeeeee"; this._bgImg1 = new ViewImage(this._ctx, 0, 0, "./bg.png"); this._bgImg1.width = 600; this._bgImg1.height = 400; this._bgImg2 = new ViewImage(this._ctx, 600, 0, "./bg.png"); this._bgImg2.width = 600; this._bgImg2.height = 400; this._bgImg2.alpha = 0.8; this._startTime = performance.now(); this._mainLoop(); } _mainLoop() { this._nowTime = performance.now(); let elapsedTime = this._nowTime - this._startTime; let idealTime = this._frame * (1000 / this._FPS); if (idealTime < elapsedTime) { this._ctx.clearRect(0, 0, this._cvs.width, this._cvs.height); // 背景画像を横スクロール let bgX = (this._bgImg1.x - this._vx) % 600; this._bgImg1.x = bgX; this._bgImg2.x = 600 + bgX; this._bgImg1.draw(); this._bgImg2.draw(); this._frame++; if (elapsedTime >= 1000) { this._startTime = this._nowTime; this._frame = 0; } } this._animID = requestAnimationFrame(this._mainLoop.bind(this)); } }
requestAnimationFrameを使ったゲームループの実装は、「【JavaScript】requestAnimationFrameでゲームループを作る」を参照。
背景用の画像を2つ用意し並べて表示。右から左へスクロールさせていく。1枚目の画像が消えたら(画像右端がキャンバス左端に到達)、X座標を元の位置(キャンバス左端)に戻している。画像同士の継ぎ目が分かるよう、2枚目の画像の透明度を下げている。
モジュロ演算子(%)は左の被除数を右の除数で割った余りを返す。割れない場合、そのまま被除数が余りとなる。
let bgX = (this._bgImg1.x - this._vx) % 600;
上のコードは、画像のX座標から速度を引き、キャンバスの幅600で割り、余りを求めている。初期位置のX座標は0、速度は1のため、X座標は−1、−2…となる。値が−600に達すると割り切れて余りが-0となり、画像が初期位置に戻る。
動きが分かりやすいよう、画像1枚目だけ表示したサンプル。
参考図書
リンク
コメント