JavaScript»ゲーム制作»EventDispatcher イベントを使ってスコアをふやす

ファイル名: js-game/quest_00925.html

下のソースコードをうつして、四角をクリックしたときにスコアがふえるプログラムを作ってください。

ソースコード

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() {
    
    this.x = 128;
    this.y = 128;
    this.destroy = false;

    // イベントリスナー(後述)を入れておく
    this.listeners = {};
  }

  // Enemy(敵)オブジェクトに何かが起こった(イベントが発生した)ときに実行したい関数を登録する
  // たとえば下記なら、敵をたおした(destroyは破壊という意味)ときにアラートを表示するという意味になる
  // 
  // addEventListener("destory", () => alert("敵をたおしたヨ"));
  //
  // イベントが発生したときに実行される関数のことをイベントリスナーとよぶ
  // イベントの発生通知を聴く者(リスナー)、ということ
  addEventListener(type, listener) {
    if (!this.listeners[type]) {
      this.listeners[type] = [];
    }
    this.listeners[type].push(listener);
  }

  // Enemy(敵)オブジェクトに、typeで指定したイベントを発生させる
  // たとえば次のように書けば、enemyがdestroy(破壊)されたというできごとが起きたことを表せる
  //
  // enemy.dispatchEvent("destroy");
  // 
  // addEventListenerで同じtype名で設定されているリスナーが実行される
  dispatchEvent(type) {
    const listeners = this.listeners[type] || [];
    listeners.forEach(listener => listener());
  }

  // x, yで指定された点がこの敵キャラの表示領域内であれば、敵をたおす
  destroyIfContains(x, y) {

    if (x > this.x && x < this.x + 32 && y > this.y && y < this.y + 32) {

      this.destroy = true;

      // destroyイベントが起きたことを、イベントリスナーに伝える
      this.dispatchEvent("destroy");
    }
  }

  update() {
    if (this.destroy) {
      this.x = Math.floor(Math.random() * (320 - 32));
      this.y = Math.floor(Math.random() * (320 - 32));
      this.destroy = false;
    }
  }

  render(context) {
    context.fillStyle = "rgb(255, 127, 127)";
    context.fillRect(this.x, this.y, 32, 32);
  }
}

const label = new ScoreLabel();
const enemy = new Enemy();

// 敵を破壊したときにスコアを10増やす
enemy.addEventListener("destroy", () => {
  label.addScore(10);
});

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;
  enemy.destroyIfContains(x, y);
});

const FPS = 60;
const frameTime = 1 / FPS;
let prevTimestamp = 0;

const update = (timestamp) => {

  const elapsed = (timestamp - prevTimestamp) / 1000;
  if (elapsed <= frameTime) {
    requestAnimationFrame(update);
    return;
  }
  prevTimestamp = timestamp;

  enemy.update();
  label.update();

  context.clearRect(0, 0, 320, 320);
  enemy.render(context);
  label.render(context);

  requestAnimationFrame(update);
};

update();

解説

addEventListener()でイベント(クリックなど)で呼び出される関数と、どんなイベントが起きたときに呼び出すかを設定します。
dispatchEvent()がイベントをが起きたことを知らせて、設定した関数を呼び出します。

このコードでは、キャンバスがクリックされたときにclickイベントが起きる(95~100行目)→もし敵の範囲内ならdestroyイベントが起きたことを知らせる(59~68行目)→destroyイベントが起きたのでスコアが10増える(88~90行目)、という順番でイベントが起きます。