【JavaScript】マウスカーソルを追いかけるマウスストーカーを作る

Canvas上の矢印がマウスカーソルを追いかけてくる。ただ追いかけるだけでなく、マウスカーソルの方向を向いて追ってくる。ベースは「【JavaScript】オブジェクトを回転させる」であり、そこに移動処理を加えている。

ArrowクラスとMousestalkerクラス

まず、簡単なCanvasに矢印を描画するArrowクラス。

/**
 * 矢印の作成と描画
 */
export class Arrow {
	constructor(parent = undefined, x = 0, y = 0) {
		if (parent !== undefined) {
			this._parent = parent;
		}
		this._x = x;
		this._y = y;
		this._color = "#ff0000";
		this._rotation = 0;

		this._init();
	}

	//////////////////////////////////
	// Private and protected
	//////////////////////////////////

	_init() {
		if (this._parent !== undefined) {
			this._ctx = this._parent;
		}
		this.draw();
	}
(mousestalker.js)
	//////////////////////////////////
	// Public
	//////////////////////////////////

	draw() {
		// 現在の描画状態を保存
		this._ctx.save();
		// コンテキストの座標を変更し、キャンバス中央に移動
		this._ctx.translate(this._x, this._y);
		// コンテキストの角度をラジアン値で指定
		this._ctx.rotate(this._rotation);

		this._ctx.fillStyle = this._color;
		this._ctx.beginPath();
		this._ctx.moveTo(-50, -25);
		this._ctx.lineTo(0, -25);
		this._ctx.lineTo(0, -50);
		this._ctx.lineTo(50, 0);
		this._ctx.lineTo(0, 50);
		this._ctx.lineTo(0, 25);
		this._ctx.lineTo(-50, 25);
		this._ctx.lineTo(-50, -25);
		this._ctx.closePath();
		this._ctx.fill();
		this._ctx.stroke();

		// save()で保存した描画状態を復元
		// 変形情報(translate、rotate)を変更前に戻している
		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 rotation() {
		return this._rotation;
	}
	set rotation(rotation) {
		this._rotation = rotation;
	}
}

次に、追いかける処理を担当するMouseStalkerクラス。

スポンサーリンク
import { Arrow } from "./arrow.js";

/**
 * マウスカーソルを追跡させる(mousestalker.js)
 */
export class MouseStalker {
	constructor() {
		this._cvs = document.getElementById('canvas');
		this._ctx = this._cvs.getContext('2d');
		this._arrow;
		this._speed = 3;
    	        this._mouseX = 0;
         	this._mouseY = 0;
		this._cvs.addEventListener('mousemove', this._onMouseMove.bind(this));
		// タイマー関連
		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._arrow = new Arrow(this._ctx);

		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 dx = this._mouseX - this._arrow.x;
                        let dy = this._mouseY - this._arrow.y;
                        let angle = Math.atan2(dy, dx);
                        let vx = Math.cos(angle) * this._speed;
                        let vy = Math.sin(angle) * this._speed;

                        this._arrow.rotation = angle;
                        this._arrow.x += vx;
                        this._arrow.y += vy;
                        this._arrow.draw();
                        
                        this._frame++;

                        if (elapsedTime >= 1000) {
				this._startTime = this._nowTime;
				this._frame = 0;
			}
		}
	
		this._animID = requestAnimationFrame(this._mainLoop.bind(this));
	}

	//////////////////////////////////
	// Handlers
	//////////////////////////////////

  _onMouseMove(e) {
    this._mouseX = e.clientX;
    this._mouseY = e.clientY;
  }
}

三角関数に関しては「【JavaScript】オブジェクトを回転させる」で解説している。

Canvasにmousemoveイベントを登録。マウスカーソルが動く度にthis._mouseX、this._mouseYに格納。Math.atan2()に渡してマウスカーソルに対する角度を求める。

ここでは角度を仮に30度とする。矢印とマウスカーソルの位置を結んだ線を斜辺とする。

この斜辺が矢印が進む道であり、進む量は速度(this._speed)を使う。直角三角形は1つの辺と1つの角度が分かれば、他全てを求められる。

さらに、カーソルに向かってX座標、Y座標上に線を伸ばしていくと直角三角形ができる。つまり、角度と斜辺の長さ(速度)が分かれば、Math.cos()とMath.sin()を使い、this._vxとthis._vyの長さが求められる。これにより1フレームレートにどの方向にどれだけ移動させられるか分かる。

コサインは隣辺と斜辺の比率(隣辺 ÷ 斜辺)だから、隣辺はコサインに斜辺を掛ければ求まる。同様にサインは対辺と斜辺の比率(対辺 ÷ 斜辺)だから、対辺はサインに斜辺を掛ければ求まる。

斜辺の長さは即ち速度だから、mousestalker.jsのコード①の部分で求まる。

this._vx = Math.cos(angle) * this._speed;
this._vy = Math.sin(angle) * this._speed;

手順をまとめると、

  1. マウスカーソルのX、Y座標を取得
  2. Math.atan2()にX、Y座標を渡し角度を求める
  3. 角度を使い矢印を回転させる
  4. コサインに速度を掛け、X座標上の移動距離を計算
  5. サインに速度を掛け、Y座標上の移動距離を計算
  6. 矢印を計算結果を元に移動

requestAnimationFrameを使ったゲームループの実装は、「【JavaScript】requestAnimationFrameでゲームループを作る」を参照。

HTML(index.html)のコード。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JavaScriptでマウスカーソルを追いかけるマウスストーカーを作る</title>
</head>
<body>
<canvas id="canvas" width="400" height="400"></canvas>
</body>
<script type="module">
import { MouseStalker } from "./mousestalker.js";
window.onload = function () {
let ms = new MouseStalker();
}
</script>
</html>

コメント

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