【JavaScript】背景画像をスクロールさせる【ゲーム制作】

ゲーム開発ではシューティングやベルトスクロールアクションなど、背景画像を延々とスクロール表示させるとがある。それを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枚目だけ表示したサンプル。

別ページで表示

参考図書

コメント

タイトルとURLをコピーしました