JavaScript»ゲーム制作»あたり判定 2つの四角のあたり判定(ゲームループ・更新メソッド使用)

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

下のソースコードをうつして、十字キーで青い四角をうごかし、赤い四角とぶつかったときにHIT!と表示されるプログラムを作ってください。

これは「2つの四角のあたり判定」を、これまで学んだゲームループ・更新メソッド・イベント通知などを使って書き直したものです。

ソースコード

class Event {
  constructor(data) {
    this.data = data;
  }
}

class Actor {

  constructor(x, y, w, h) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.listeners = {};
  }

  addEventListener(type, listener) {
    if (!this.listeners[type]) {
      this.listeners[type] = [];
    }
    this.listeners[type].push(listener);
  }

  dispatchEvent(type, e) {
    const listeners = this.listeners[type] || [];
    listeners.forEach(listener => listener(e));
  }

  isHit(other) {
    return this.x < other.x + other.w && 
      other.x < this.x + this.w && 
      this.y < other.y + other.h && 
      other.y < this.y + this.h;
  }
}

class Player extends Actor {

  update(input) {
    if (input["ArrowUp"]) this.y -= 4;
    if (input["ArrowRight"]) this.x += 4;
    if (input["ArrowDown"]) this.y += 4;
    if (input["ArrowLeft"]) this.x -= 4;
  }

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

class Enemy extends Actor {

  update(input) {
  }

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

class HitLabel {

  constructor() {
    this.hit = false;
  }

  update() {
  }

  render(context) {
    if (this.hit) {
      context.fillStyle = "rgb(127, 127, 127)";
      context.font = "32px monospace";
      context.textAlign = "center"
      context.fillText("HIT!", 160, 300);
    }
  }
}

const player = new Player(144, 144, 32, 32);
const enemy = new Enemy(192, 96, 32, 32);
const label = new HitLabel();

player.addEventListener("hit", e => {
  label.hit = true;
});
player.addEventListener("deviate", e => {
  label.hit = false;
});

const input = {};
addEventListener("keydown", e => input[e.key] = true);
addEventListener("keyup", e => input[e.key] = false);


const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");

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;

  player.update(input);
  enemy.update(input);
  label.update(input);

  // あたり判定
  if (player.isHit(enemy)) {
    player.dispatchEvent("hit", new Event());
  } else {
    player.dispatchEvent("deviate", new Event());
  }

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

  requestAnimationFrame(update);
};

update();

解説

PlayerクラスとEnemyクラスは、Actorクラスを引き継いで作られています。このような場合、Actorクラスを「親クラス」や「スーパークラス」、PlayerクラスとEnemyクラスを「子クラス」や「サブクラス」と呼びます。
子クラスは親クラスのプロパティを全て引き継ぎます。子クラス独自のプロパティを追加することもできます。