JavaScript»ゲーム制作»EventDispatcher イベントを使って、倒した敵を画面から消す
ファイル名: js-game/quest_00927.html
下のソースコードをうつしてプログラムを作ってください。四角は1秒に1つずつふえていき、クリックしたら画面から消えてスコアがふえます。
ソースコード
// イベントを表すクラス
// 好きなものを入れておくことのできるdataというプロパティを用意する
class GameEvent {
constructor(data) {
this.data = data;
}
}
class ScoreLabel {
constructor() {
this.score = 0;
}
update() {
}
render(context) {
context.fillStyle = "rgb(0, 0, 0)";
context.font = "24px monospace";
context.fillText("SCORE: " + this.score, 0, 24);
}
addScore(score) {
this.score += score;
}
}
class Enemy {
constructor(x, y) {
this.x = x;
this.y = y;
this.listeners = {};
}
addEventListener(type, listener) {
if (!this.listeners[type]) {
this.listeners[type] = [];
}
this.listeners[type].push(listener);
}
// イベントを発生させるときに、イベントを表すEventオブジェクトをいっしょに渡せるようにする
// Eventオブジェクトには好きなデータを入れることができるので、
// イベントを発生させたのは誰(どのオブジェクトか)などの情報を
// イベントリスナーに渡すために使うことができる
dispatchEvent(type, event) {
const listeners = this.listeners[type] || [];
listeners.forEach(listener => listener(event));
}
destroyIfContains(x, y) {
if (x > this.x && x < this.x + 32 && y > this.y && y < this.y + 32) {
// destroyイベントが発生したことをイベントリスナーに伝える
// その際、自分自身(つまり、クリックして倒されたEnemyオブジェクト)を
// Eventオブジェクトに入れて、イベントリスナーに渡す
this.dispatchEvent("destroy", new GameEvent(this));
}
}
update(input) {
}
render(context) {
context.fillStyle = "rgb(255, 127, 127)";
context.fillRect(this.x, this.y, 32, 32);
}
}
const label = new ScoreLabel();
let enemies = [];
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
canvas.addEventListener("click", (e) => {
const rect = e.target.getBoundingClientRect();
const x = e.clientX - rect.left - 1;
const y = e.clientY - rect.top - 1;
enemies.forEach(enemy => enemy.destroyIfContains(x, y));
});
const FPS = 60;
const frameTime = 1 / FPS;
let prevTimestamp = 0;
let tick = 0;
const update = (timestamp) => {
const elapsed = (timestamp - prevTimestamp) / 1000;
if (elapsed <= frameTime) {
requestAnimationFrame(update);
return;
}
prevTimestamp = timestamp;
tick++;
// 1秒に1回、あたらしいEnemyオブジェクトをふやしてenemies配列に入れる
if (tick % FPS === 0) {
const x = Math.floor(Math.random() * (320 - 32));
const y = Math.floor(Math.random() * (320 - 32));
const enemy = new Enemy(x, y);
// 敵を破壊したらスコアを10増やし、
// 破壊されたEnemyオブジェクトはenemiesから削除する
// eはdispatchEventを呼び出したときに渡したEventオブジェクトで、
// 中(e.data)には破壊されたEnemyオブジェクト自身が入っている
enemy.addEventListener("destroy", (e) => {
label.addScore(10);
enemies = enemies.filter(enemy => enemy !== e.data);
});
enemies.push(enemy);
}
enemies.forEach(enemy => enemy.update());
label.update();
context.clearRect(0, 0, 320, 320);
enemies.forEach(enemy => enemy.render(context));
label.render(context);
requestAnimationFrame(update);
};
update();
解説
配列enemiesを使って四角形(enemy)を管理します。112行目のfilterは、配列の要素のうちで条件に合う要素だけを新しい配列にします。
キャンバスがクリックされたとき、配列の四角形すべてに当たりか判定をします(77~82行目)。当たり判定のでた(クリックされた)四角形のデータはイベントリスナーに渡されます(53~60行目)。配列enemiesのうち、渡された(=クリックされた)四角形(enemy)以外が新しい配列enemiesになります(110~113行目)。