大鱼吃泡泡小游戏(一)

240 阅读1分钟

利用大鱼吃泡泡小游戏主要学习是规则图形中圆形的碰撞测试

2023-04-01 21.40.38.gif

主要利用勾股定理:

判断两个图形的距离和两个图形半径相加的值的比较

Math.pow(3,2)+Math.pow(4,2)=Math.pow(5,2)

实例代码:

// canvas setup
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
canvas.width = 800
canvas.height = 500
let score = 0
let gameFrame = 0
ctx.font = 'bold 48px serif'

const canvasPosition = canvas.getBoundingClientRect()

// mouse
const mouse = {
  x: canvas.width / 2,
  y: canvas.height / 2,
  click: false
}

canvas.addEventListener('mousedown', (event) => {
  mouse.click = true
  mouse.x = event.x - canvasPosition.left
  mouse.y = event.y - canvasPosition.top
})

canvas.addEventListener('mouseup', () => {
  mouse.click = false
})

const playerLeft = new Image()
playerLeft.src = 'image/play_fish_left.png'
const playerRight = new Image()
playerRight.src = 'image/play_fish_right.png'

class Player {
  constructor() {
    this.x = canvas.width
    this.y = canvas.height / 2
    this.radius = 50
    this.angle = 0
    this.frameX = 0
    this.frameY = 0
    this.frame = 0
    this.spriteWidth = 498
    this.spriteHeight = 327
  }

  update() {
    // 比较当前的位置和鼠标的位置
    const dx = this.x - mouse.x
    const dy = this.y - mouse.y
    this.angle = dx > 0 ? Math.atan2(dy, dx) : Math.atan2(dy, dx) - Math.PI
    if (mouse.x != this.x) {
      this.x -= dx / 30
    }
    if (mouse.y != this.y) {
      this.y -= dy / 30
    }
  }

  drow() {
    if (mouse.click) {
      ctx.lineWidth = 0.2
      ctx.beginPath()
      ctx.moveTo(this.x, this.y)
      ctx.lineTo(mouse.x, mouse.y)
      ctx.stroke()
    }
    ctx.save()
    ctx.translate(this.x, this.y)
    ctx.rotate(this.angle)
    ctx.drawImage(
      this.x >= mouse.x ? playerLeft : playerRight,
      this.frameX * this.spriteWidth,
      this.frameY * this.spriteHeight,
      this.spriteWidth,
      this.spriteHeight,
      -60,
      -45,
      this.spriteWidth / 4,
      this.spriteHeight / 4
    )
    ctx.restore()
  }
}

const player = new Player()

const bubble = new Image()
bubble.src = 'image/bubble_pop_one/bubble_pop_frame_01.png'
// 气泡
const bubblesArray = []
class Bubble {
  constructor() {
    this.x = Math.random() * canvas.width
    this.y = canvas.height + 100
    this.width = 120;
    this.height = 120;
    this.radius = 50
    this.speed = Math.random() * 5 + 1
    this.distance
    this.counted = false
    this.sound = Math.random() <= 0.5 ? 'sound1' : 'sound2'
  }
  update() {
    this.y -= this.speed
    // 三角函数勾股定理计算一下distance的值 Math.pow(3,2)+Math.pow(4,2)=Math.pow(5,2)
    const dx = this.x - player.x
    const dy = this.y - player.y
    this.distance = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2))
  }
  draw() {
    // ctx.fillStyle = 'blue'
    // ctx.beginPath()
    // ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2)
    // ctx.fill()
    // ctx.closePath()
    // ctx.stroke()
    ctx.save()
    ctx.translate(this.x, this.y)
    ctx.drawImage(
      bubble,
      -60,
      -60,
      this.width,
      this.height
    )
    ctx.restore()
  }
}

// 随机切换气泡音
const sound1 = document.createElement('audio')
sound1.src = 'audio/y1332.wav'
const sound2 = document.createElement('audio')
sound2.src = 'audio/y826.wav'

function handleBubbles() {
  if (gameFrame % 50 === 0) {
    bubblesArray.push(new Bubble())
  }
  for (let i = 0; i < bubblesArray.length; i++) {
    bubblesArray[i].update()
    bubblesArray[i].draw()

    // 会有闪烁的问题,需要放到另一个for循环中
    if (bubblesArray[i]) {
      if (bubblesArray[i].y < 0 - this.radius / 2) {
        bubblesArray.splice(i, 1)
      }

      // 碰撞测试
      if (bubblesArray[i].distance < player.radius + bubblesArray[i].radius) {
        if (!bubblesArray[i].counted) {
          if (bubblesArray[i].sound === 'sound1') {
            sound1.play()
          } else {
            sound2.play()
          }
          score++
          bubblesArray[i].counted = true
          bubblesArray.splice(i, 1)
        }
      }
    }
  }
}

function animation() {
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  handleBubbles()
  player.update()
  player.drow()
  ctx.fillStyle = 'black'
  ctx.fillText('score' + score, 10, 50)
  gameFrame++
  requestAnimationFrame(animation)
}

animation()