在 javascript "catch" 游戏中实施碰撞检测以计分

Implement collision detection for scoring on a javascript "catch" game

我正在使用 canvas 开发一个非常简单的 JS 游戏,但我很难实现得分点的碰撞检测。

该游戏是接球游戏,因此,每当玩家控制的屏幕底部的方块击中其中一个落球时,得分计数应更新 1。我在网上寻找了许多不同的方法来做到这一点,但到目前为止没有运气。

游戏有三个不同的对象,Ball、Player 和 Score,每个对象都有自己的绘制和更新函数。我最近一直在尝试通过将碰撞检测放在乐谱的更新函数中来实现碰撞检测,但无济于事。

你会怎么做?

代码如下:

const canvas = document.querySelector('canvas')
const c = canvas.getContext('2d')

canvas.width = 500
canvas.height = 800

//Variables

//keyboard events
let leftPressed = false
let rightPressed = false
//ball
let moveSpeed = 5
let balls = []
//player
let player
let pWidth = 60
let pHeight = 20
let color = 'black'
//score
let score
let x = canvas.width / 1.5
let y = 30
let points = 0


//Event listeners

//move
document.addEventListener("keydown", keyDownHandler, false);

function keyDownHandler(e) {
  if (e.key == "Right" || e.key == "ArrowRight") {
    rightPressed = true;
  } else if (e.key == "Left" || e.key == "ArrowLeft") {
    leftPressed = true;
  }
}

//stop
document.addEventListener("keyup", keyUpHandler, false);

function keyUpHandler(e) {
  if (e.key == "Right" || e.key == "ArrowRight") {
    rightPressed = false;
  } else if (e.key == "Left" || e.key == "ArrowLeft") {
    leftPressed = false;
  }
}

//Objects
function Ball(x, y, dy, radius, color) {
  this.x = x
  this.y = y
  this.dy = dy
  this.radius = radius
  this.color = color

  this.draw = function() {
    c.beginPath()
    c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false)
    c.strokeStyle = 'black'
    c.stroke()
    c.fillStyle = this.color
    c.fill()
    c.closePath()
  }

  this.update = function() {
    this.y += dy
    this.draw()
  }

}

function Player(x, y, pWidth, pHeight, color) {
  this.x = x
  this.y = y
  this.pWidth = pWidth
  this.pHeight = pHeight
  this.color = color

  this.draw = function() {
    c.fillStyle = this.color
    c.fillRect(this.x, this.y, this.pWidth, this.pHeight)

  }

  this.update = function() {
    //move player

    if (leftPressed && this.x > 0) {
      this.x -= moveSpeed
    } else if (rightPressed && this.x < canvas.width - pWidth) {
      this.x += moveSpeed
    }

    this.draw()
  }
}

function Score(x, y, points) {
  this.x = x
  this.y = y
  this.points = 0

  this.draw = function() {
    c.font = '30px Helvetica'
    c.fillStyle = '#000'
    c.fillText(points, this.x, this.y)
  }

  this.update = function() {
    if (balls.x >= player.x && balls.x + balls.radius <= player.x + player.pWidth) {
      this.points += 1
    }
    this.draw()
  }
}


// Initialize
function init() {
  //Initialize balls
  for (i = 0; i < 40; i++) {
    let x = Math.random() * canvas.width
    let y = Math.random() * canvas.height
    let dy = 3
    let radius = 15
    let color = 'purple'

    balls.push(new Ball(x, y, dy, radius, color))
  }

  //Initialize player
  player = new Player((canvas.width / 2) - (pWidth / 2),
    canvas.height - pHeight,
    pWidth,
    pHeight,
    color)

  //Initialize score
  score = new Score(x, y, 'Score: ' + points)
}

// Animate
function animate() {
  requestAnimationFrame(animate)
  c.clearRect(0, 0, canvas.width, canvas.height)

  for (i = 0; i < balls.length; i++) {
    balls[i].update()
  }

  player.update()

  score.update()


}

init()
animate()

您可能希望在可以访问所有可碰撞对象的主循环中的某处进行碰撞检测。假设只有球和玩家发生碰撞,那么 in the loop with balls 是一个很好的选择。根据您想要的碰撞类型,创建一个接受球对象并将其与玩家进行比较的函数。最简单的命中检测是基于每个对象的 size/radius 的简单距离,但存在其他更高级的方法。

// Animate
function animate() {
  requestAnimationFrame(animate);
  c.clearRect(0, 0, canvas.width, canvas.height);

  for (i = 0; i < balls.length; i++) {
    balls[i].update();
    if (detectHit(ball[i], player)) {
      // hit was detected, do stuff
    }
  }

  player.update();
  score.update();
}

function detectHit(ball, player) {
  const ballRadius = ball.radius;
  const ballCenter = {x: ball.x, y: ball.y};
  
  const playerRadius = Math.min(player.pWidth, player.pHeight);
  const playerCenter = {
    x: player.x + player.pWidth / 2,
    y: player.y + player.pHeight / 2
  };
  
  // distance = sqr((x1 - x2)^2 + (y1 - y2)^2)
  const distanceSqrd = (ballCenter.x - playerCenter.x) ** 2 + (ballCenter.y - playerCenter.y) ** 2;
  const radiusDistanceSqrd = (ballRadius + playerRadius) ** 2;
  
  /*
    Instead of getting the square root, save a complex 
    calculation by comparing the squared distances directly
  */
  return distanceSqrd <= radiusDistanceSqrd;
}

const balls = [
  {
    x: 10,
    y: 10,
    radius: 5
  },
  {
    x: 50,
    y: 10,
    radius: 5
  },
];

const player = {
  x: 20,
  y: 10,
  pWidth: 15,
  pHeight: 15,
};

balls.forEach(ball => console.log('Hit detected: ', detectHit(ball, player)));