Реализовать обнаружение столкновений для оценки в javascript-игре «catch» - PullRequest
0 голосов
/ 31 декабря 2018

Я работаю над очень простой JS-игрой с использованием canvas, но мне очень трудно реализовать обнаружение столкновений для начисления очков.

Игра - игра-ловушка, поэтому каждый раз, когдаКонтролируемый игроком блок в нижней части экрана поражает один из падающих шаров, счетчик очков должен быть обновлен на один.Я искал в Интернете много разных способов сделать это, но пока не повезло.

В игре есть три разных объекта, Ball, Player и Score, каждый из которых имеет свои собственные функции рисования и обновления.Самый последний способ, которым я пытался реализовать обнаружение столкновений, это поместить его в функцию обновления 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()

1 Ответ

0 голосов
/ 31 декабря 2018

Скорее всего, вы захотите выполнить обнаружение столкновений где-нибудь в вашем основном цикле, который имеет доступ ко всем вашим объектам, которые могут столкнуться.Если предположить, что сталкиваются только шары и игрок, то в петле с шарами хороший кандидат.В зависимости от того, какое столкновение вы хотите, создайте функцию, которая берет объект шара и сравнивает его с игроком.Самое простое обнаружение попадания - это простое расстояние, основанное на размере / радиусе каждого объекта, но существуют и другие более продвинутые способы.

// 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)));
...