zoukankan      html  css  js  c++  java
  • 【默默努力】h5-game-blockBreaker

    先放下游戏的效果,我不太会玩游戏

    然后放下无私开源的作者大大的地址:https://github.com/yangyunhe369/h5-game-blockBreaker
    这个游戏的话,我觉得应该是如果如果球跟砖碰到了,那么这个砖就消失,然后得一分,然后这个球就会以竖直的相同的角度返回,
    如果球到了发射台,就会以在发射台的角度返回去,如果球没有碰到发射台,那么球就沿着坠落的方向消失,游戏结束。
    接下来我们看代码
    如果我写的话,应该分数是一个,官卡是一个,砖是一个幕布,下面的发射台是一个,球是一个。应该是5个,然后再加一个功能的js,6个js去实现这个。
    我们来看看作者大大怎么实现的吧~
    入口的文件html,里面引用了common.js,scene.js 还有game.js,canvas绘制了游戏的背景大小

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>打砖块v1.1</title>
      <link rel="stylesheet" href="css/common.css">
      <link rel="stylesheet" href="css/style.css">
    </head>
    <body>
    <canvas id="canvas" width="1000" height="500"></canvas>
    <div>使用左右方向键,进行移动;空格键发射小球并开始游戏,游戏结束时按空格键重置游戏;P 键暂停游戏;通关游戏后,按 N 键可进入下一关卡</div>
    <script src="js/common.js"></script>
    <script src="js/scene.js"></script>
    <script src="js/game.js"></script>
    <script src="js/main.js"></script>
    </body>
    </html>
    

    common.js中的代码写的真好,都看的懂,而且作者大大有注释,真的非常注重浏览器兼容性。

    //common.js
    /* by:弦云孤赫——David Yang
    ** github - https://github.com/yangyunhe369
    */
    // 封装打印日志方法
    const log = console.log.bind(console)
    // 生成图片对象方法
    const imageFromPath = function (src) {
      let img = new Image()
      img.src = './images/' + src
      return img
    }
    // 检测页面不可见时自动暂停游戏方法
    const isPageHidden = function (game) {
      let hiddenProperty = 'hidden' in document ? 'hidden' :    
          'webkitHidden' in document ? 'webkitHidden' :    
          'mozHidden' in document ? 'mozHidden' :    
          null
      let visibilityChangeEvent = hiddenProperty.replace(/hidden/i, 'visibilitychange')
      // 监听页面是否可见事件
      document.addEventListener(visibilityChangeEvent, function () {
        if (!document[hiddenProperty]) {  // 可见状态
          setTimeout(function () {
            game.state = game.state_RUNNING
          }, 100)
        } else { // 不可见状态
          game.state = game.state_STOP
        }
      })
    }
    // 图片素材路径
    const allImg = {
      background: 'background.jpg',
      paddle: 'paddle.png',
      ball: 'ball.png',
      block1: 'block001.png',
      block2: 'block002.png',
    }
    

    main.js中就是游戏的初始化

    //main.js
    /* by:弦云孤赫——David Yang
    ** github - https://github.com/yangyunhe369
    */
    // 游戏主函数
    let _main = {
      LV: 1,                               // 初始关卡
      MAXLV: 2,                            // 最终关卡
      scene: null,                         // 场景对象
      blockList: null,                     // 所有砖块对象集合
      ball: null,                          // 小球对象
      paddle: null,                        // 挡板对象
      score: null,                         // 计分板对象
      ball_x: 491,                         // 小球默认x轴坐标
      ball_y: 432,                         // 小球默认y轴坐标
      paddle_x: 449,                       // 挡板默认x轴坐标
      paddle_y: 450,                       // 挡板默认y轴坐标
      score_x: 10,                         // 计分板默认x轴坐标
      score_y: 30,                         // 计分板默认y轴坐标
      fps: 60,                             // 游戏运行帧数
      game: null,                          // 游戏主要逻辑对象
      start: function () {                 // 游戏启动函数
        let self = this
        /**
         * 生成场景(根据游戏难度级别不同,生成不同关卡)
         */
        self.scene = new Scene(self.LV)
        // 实例化所有砖块对象集合
        self.blockList = self.scene.initBlockList()
        /**
         * 小球
         */
        self.ball = new Ball(self)
        /**
         * 挡板
         */
        self.paddle = new Paddle(self)
        /**
         * 计分板
         */
        self.score = new Score(self)
        /**
         * 游戏主要逻辑
         */
        self.game = new Game(self)
        /**
         * 游戏初始化
         */
        self.game.init(self)
      }
    }
    _main.start()
    

    scene.js里面定义的是各种对象及方法

    /* by:弦云孤赫——David Yang
    ** github - https://github.com/yangyunhe369
    */
    // 定义挡板对象
    class Paddle {
      constructor (_main) {
        let p = {
          x: _main.paddle_x,                                   // x轴坐标
          y: _main.paddle_y,                                   // y轴坐标
          w: 102,                                              // 图片宽度
          h: 22,                                               // 图片高度
          speed: 10,                                           // x轴移动速度
          ballSpeedMax: 8,                                     // 小球反弹速度最大值
          image: imageFromPath(allImg.paddle),                 // 引入图片对象
          isLeftMove: true,                                    // 能否左移
          isRightMove: true,                                   // 能否右移
        }
        Object.assign(this, p)
      }
      moveLeft () {
        this.x -= this.speed
      }
      moveRight () {
        this.x += this.speed
      }
      // 小球、挡板碰撞检测
      collide (ball) {
        let b = ball
        let p = this
        if (Math.abs((b.x + b.w/2) - (p.x + p.w/2)) < (b.w + p.w)/2 &&
            Math.abs((b.y + b.h/2) - (p.y + p.h/2)) < (b.h + p.h)/2) {
          return true
        }
        return false
      }
      // 计算小球、挡板碰撞后x轴速度值
      collideRange (ball) {
        let b = ball
        let p = this
        let rangeX = 0
        rangeX = (p.x + p.w/2) - (b.x + b.w/2)
        if (rangeX < 0) { // 小球撞击挡板左侧
          return rangeX / (b.w/2 + p.w/2) * p.ballSpeedMax
        } else if (rangeX > 0) { // 小球撞击挡板右侧
          return rangeX / (b.w/2 + p.w/2) * p.ballSpeedMax
        }
      }
    }
    // 小球对象
    class Ball {
      constructor (_main) {
        let b = {
          x: _main.ball_x,                      // x轴坐标
          y: _main.ball_y,                      // y轴坐标
          w: 18,                                // 图片宽度
          h: 18,                                // 图片高度
          speedX: 1,                            // x轴速度
          speedY: 5,                            // y轴速度
          image: imageFromPath(allImg.ball),    // 图片对象
          fired: false,                         // 是否运动,默认静止不动
        }
        Object.assign(this, b)
      }
      move (game) {
        if (this.fired) {
          // 碰撞边界检测
          if (this.x < 0 || this.x > 1000 - this.w) {
            this.speedX *= -1
          }
          if (this.y < 0) {
            this.speedY *= -1
          }
          if (this.y > 500 - this.h) {
            // 游戏结束
            game.state = game.state_GAMEOVER
            // game.isGameOver = true
          }
          // 移动
          this.x -= this.speedX
          this.y -= this.speedY
        }
      }
    }
    // 砖块
    class Block {
      constructor (x, y, life = 1) {
        let bk = {
          x: x,                                                                               // x轴坐标
          y: y,                                                                               // y轴坐标
          w: 50,                                                                              // 图片宽度
          h: 20,                                                                              // 图片高度
          image: life == 1 ? imageFromPath(allImg.block1) : imageFromPath(allImg.block2),     // 图片对象
          life: life,                                                                         // 生命值
          alive: true,                                                                        // 是否存活
        }
        Object.assign(this, bk)
      }
      // 消除砖块
      kill () {
        this.life--
        if (this.life == 0) {
          this.alive = false
        } else if (this.life == 1) {
          this.image = imageFromPath(allImg.block1)
        }
      }
      // 小球、砖块碰撞检测
      collide (ball) {
        let b = ball
        if (Math.abs((b.x + b.w/2) - (this.x + this.w/2)) < (b.w + this.w)/2 &&
            Math.abs((b.y + b.h/2) - (this.y + this.h/2)) < (b.h + this.h)/2) {
          this.kill()
          return true
        } else {
          return false
        }
      }
      // 计算小球、砖块碰撞后x轴速度方向
      collideBlockHorn (ball) {
        let b = ball    // 小球
        let bk = this   // 砖块
        let rangeX = 0
        let rangeY = 0
        rangeX = Math.abs((b.x + b.w/2) - (bk.x + bk.w/2))
        rangeY = Math.abs((b.y + b.h/2) - (bk.y + bk.h/2))
        if (rangeX > bk.w/2 && rangeX < (bk.w/2 + b.w/2) && rangeY < (bk.h/2 + b.h/2)) { // X轴方向与砖块四角相交
          if (b.x < bk.x && b.speedX > 0 || b.x > bk.x && b.speedX < 0) { // 小球在砖块左侧时
            return false
          } else { // 小球在砖块右侧
            return true
          }
        }
        return false
      }
    }
    // 计分板
    class Score {
      constructor (_main) {
        let s = {
          x: _main.score_x,                               // x轴坐标
          y: _main.score_y,                               // y轴坐标
          text: '分数:',                                 // 文本分数
          textLv: '关卡:',                               // 关卡文本
          score: 200,                                     // 每个砖块对应分数
          allScore: 0,                                    // 总分
          blockList: _main.blockList,                     // 砖块对象集合
          blockListLen: _main.blockList.length,           // 砖块总数量
          lv: _main.LV,                                   // 当前关卡
        }
        Object.assign(this, s)
      }
      // 计算总分
      computeScore () {
        let num = 0
        let allNum = this.blockListLen
        num = this.blockListLen - this.blockList.length
        this.allScore = this.score * num
      }
    }
    // 定义场景
    class Scene {
      constructor (lv) {
        let s = {
          lv: lv,                                         // 游戏难度级别
          canvas: document.getElementById("canvas"),      // canvas对象
          blockList: [],                                  // 砖块坐标集合
        }
        Object.assign(this, s)
      }
      // 实例化所有砖块对象
      initBlockList () {
        this.creatBlockList()
        let arr = []
        for (let item of this.blockList) {
          for (let list of item) {
            if (list.type === 1) {
              let obj = new Block(list.x, list.y)
              arr.push(obj)
            } else if (list.type === 2) {
              let obj = new Block(list.x, list.y, 2)
              arr.push(obj)
            }
          }
        }
        return arr
      }
      // 创建砖块坐标二维数组,并生成不同关卡
      creatBlockList () {
        let lv = this.lv,                         // 游戏难度级别
            c_w = this.canvas.width,              // canvas宽度
            c_h = this.canvas.height,             // canvas高度
            xNum_max = c_w/50,                    // x轴砖块最大数量
            yNum_max = 12,                        // y轴砖块最大数量
            x_start = 0,                          // x轴起始坐标,根据砖块数量浮动
            y_start = 60                          // y轴起始坐标,默认从60起
    
        switch (lv) {
          case 1 : // 正三角形
            var xNum = 16,                               // x轴砖块第一层数量
                yNum = 9                                 // y轴砖块层数
            // 循环y轴
            for(let i = 0;i < yNum;i++){
              let arr = []
              // 修改每层x轴砖块数量
              if (i === 0) {
                xNum = 1
              } else if (i === 1) {
                xNum = 2
              } else {
                xNum += 2
              }
              x_start = (xNum_max - xNum)/2 * 50             // 修改每层x轴砖块起始坐标
              // 循环x轴
              for(let k = 0;k < xNum;k++){
                if (i < 3) { // 前三排为特殊砖块
                  arr.push({
                    x: x_start + k*50,
                    y: y_start + i*20,
                    type: 2,
                  })
                } else {
                  arr.push({
                    x: x_start + k*50,
                    y: y_start + i*20,
                    type: 1,
                  })
                }
              }
              this.blockList.push(arr)
            }
            break
          case 2 :  // 倒三角形
            var xNum = 16,                              // x轴砖块第一层数量
                yNum = 9                                // y轴砖块层数
            // 循环y轴
            for(let i = 0;i < yNum;i++){
              let arr = []
              // 修改每层x轴砖块数量
              if (i === yNum - 1) {
                xNum = 1
              } else if (i === 0) {
                xNum = xNum
              } else {
                xNum -= 2
              }
              x_start = (xNum_max - xNum)/2 * 50             // 修改每层x轴砖块起始坐标
              // 循环x轴
              for(let k = 0;k < xNum;k++){
                if (i < 3) { // 前三排为特殊砖块
                  arr.push({
                    x: x_start + k*50,
                    y: y_start + i*20,
                    type: 2,
                  })
                } else {
                  arr.push({
                    x: x_start + k*50,
                    y: y_start + i*20,
                    type: 1,
                  })
                }
              }
              this.blockList.push(arr)
            }
            break
          case 3 : // 工字形
            var xNum = 16,                              // x轴砖块第一层数量
                yNum = 9                                // y轴砖块层数
            // 循环y轴
            for(let i = 0;i < yNum;i++){
              let arr = []
              // 修改每层x轴砖块数量
              if (i === 0) {
                xNum = xNum
              } else if (i > 4) {
                xNum += 2
              } else {
                xNum -= 2
              }
              x_start = (xNum_max - xNum)/2 * 50             // 修改每层x轴砖块起始坐标
              // 循环x轴
              for(let k = 0;k < xNum;k++){
                if (i < 3) { // 前三排为特殊砖块
                  arr.push({
                    x: x_start + k*50,
                    y: y_start + i*20,
                    type: 2,
                  })
                } else {
                  arr.push({
                    x: x_start + k*50,
                    y: y_start + i*20,
                    type: 1,
                  })
                }
              }
              this.blockList.push(arr)
            }
            break
        }
      }
    }
    

    game.js中进行的是一些游戏初始化,以及必备说明

    /* by:弦云孤赫——David Yang
    ** github - https://github.com/yangyunhe369
    */
    // 游戏主要运行逻辑
    class Game {
      constructor (main) {
        let g = {
          main: main,                                                   // 游戏主函数
          actions: {},                                                  // 记录按键动作
          keydowns: {},                                                 // 记录按键keycode
          state: 1,                                                     // 游戏状态值,初始默认为1
          state_START: 1,                                               // 开始游戏
          state_RUNNING: 2,                                             // 游戏开始运行
          state_STOP: 3,                                                // 暂停游戏
          state_GAMEOVER: 4,                                            // 游戏结束
          state_UPDATE: 5,                                              // 游戏通关
          canvas: document.getElementById("canvas"),                    // canvas元素
          context: document.getElementById("canvas").getContext("2d"),  // canvas画布
          timer: null,                                                  // 轮询定时器
          fps: main.fps,                                                // 动画帧数,默认60
        }
        Object.assign(this, g)
      }
      // 绘制页面所有素材
      draw (paddle, ball, blockList, score) {
        let g = this
        // 清除画布
        g.context.clearRect(0, 0, g.canvas.width, g.canvas.height)
        // 绘制背景图
        g.drawBg()
        // 绘制挡板
        g.drawImage(paddle)
        // 绘制小球
        g.drawImage(ball)
        // 绘制砖块
        g.drawBlocks(blockList)
        // 绘制分数
        g.drawText(score)
      }
      // 绘制图片
      drawImage (obj) {
        this.context.drawImage(obj.image, obj.x, obj.y)
      }
      // 绘制背景图
      drawBg () {
        let bg = imageFromPath(allImg.background)
        this.context.drawImage(bg, 0, 0)
      }
      // 绘制所有砖块
      drawBlocks (list) {
        for (let item of list) {
          this.drawImage(item)
        }
      }
      // 绘制计数板
      drawText (obj) {
        this.context.font = '24px Microsoft YaHei'
        this.context.fillStyle = '#fff'
        // 绘制分数
        this.context.fillText(obj.text + obj.allScore, obj.x, obj.y)
        // 绘制关卡
        this.context.fillText(obj.textLv + obj.lv, this.canvas.width - 100, obj.y)
      }
      // 游戏结束
      gameOver () {
        // 清除定时器
        clearInterval(this.timer)
        // 清除画布
        this.context.clearRect(0, 0, this.canvas.width, this.canvas.height)
        // 绘制背景图
        this.drawBg()
        // 绘制提示文字
        this.context.font = '48px Microsoft YaHei'
        this.context.fillStyle = '#fff'
        this.context.fillText('游戏结束', 404, 226)
      }
      // 游戏晋级
      goodGame () {
        // 清除定时器
        clearInterval(this.timer)
        // 清除画布
        this.context.clearRect(0, 0, this.canvas.width, this.canvas.height)
        // 绘制背景图
        this.drawBg()
        // 绘制提示文字
        this.context.font = '48px Microsoft YaHei'
        this.context.fillStyle = '#fff'
        this.context.fillText('恭喜晋级下一关卡', 308, 226)
      }
      // 游戏通关
      finalGame () {
        // 清除定时器
        clearInterval(this.timer)
        // 清除画布
        this.context.clearRect(0, 0, this.canvas.width, this.canvas.height)
        // 绘制背景图
        this.drawBg()
        // 绘制提示文字
        this.context.font = '48px Microsoft YaHei'
        this.context.fillStyle = '#fff'
        this.context.fillText('恭喜通关全部关卡', 308, 226)
      }
      // 注册事件
      registerAction (key, callback) {
        this.actions[key] = callback
      }
      // 小球碰撞砖块检测
      checkBallBlock (g, paddle, ball, blockList, score) {
        let p = paddle, b = ball
        // 小球碰撞挡板检测
        if (p.collide(b)) {
          // 当小球运动方向趋向挡板中心时,Y轴速度取反,反之则不变
          if (Math.abs(b.y + b.h/2 - p.y + p.h/2) > Math.abs(b.y + b.h/2 + b.speedY - p.y + p.h/2)) {
            b.speedY *= -1
          } else {
            b.speedY *= 1
          }
          // 设置X轴速度
          b.speedX = p.collideRange(b)
        }
        // 小球碰撞砖块检测
        blockList.forEach(function (item, i, arr) {
          if (item.collide(b)) { // 小球、砖块已碰撞
            if (!item.alive) { // 砖块血量为0时,进行移除
              arr.splice(i, 1)
            }
            // 当小球运动方向趋向砖块中心时,速度取反,反之则不变
            if ((b.y < item.y && b.speedY < 0) || (b.y > item.y && b.speedY > 0)) {
              if (!item.collideBlockHorn(b)) {
                b.speedY *= -1
              } else { // 当小球撞击砖块四角时,Y轴速度不变
                b.speedY *= 1
              }
            } else {
              b.speedY *= 1
            }
            // 当小球撞击砖块四角时,X轴速度取反
            if (item.collideBlockHorn(b)) {
              b.speedX *= -1
            }
            // 计算分数
            score.computeScore()
          }
        })
        // 挡板移动时边界检测
        if (p.x <= 0) { // 到左边界时
          p.isLeftMove = false
        } else {
          p.isLeftMove = true
        }
        if (p.x >= 1000 - p.w) { // 到右边界时
          p.isRightMove = false
        } else {
          p.isRightMove = true
        }
        // 移动小球
        b.move(g)
      }
      // 设置逐帧动画
      setTimer (paddle, ball, blockList, score) {
        let g = this
        let main = g.main
        g.timer = setInterval(function () {
          // actions集合
          let actions = Object.keys(g.actions)
          for (let i = 0; i < actions.length; i++) {
            let key = actions[i]
            if(g.keydowns[key]) {
              // 如果按键被按下,调用注册的action
              g.actions[key]()
            }
          }
          // 当砖块数量为0时,挑战成功
          if (blockList.length == 0) {
            if (main.LV === main.MAXLV) { // 最后一关通关
              // 升级通关
              g.state = g.state_UPDATE
              // 挑战成功,渲染通关场景
              g.finalGame()
            } else { // 其余关卡通关
              // 升级通关
              g.state = g.state_UPDATE
              // 挑战成功,渲染下一关卡场景
              g.goodGame()
            }
          }
          // 判断游戏是否结束
          if (g.state === g.state_GAMEOVER) {
            g.gameOver()
          }
          // 判断游戏开始时执行事件
          if (g.state === g.state_RUNNING) {
            g.checkBallBlock(g, paddle, ball, blockList, score)
            // 绘制游戏所有素材
            g.draw(paddle, ball, blockList, score)
          } else if (g.state === g.state_START){
            // 绘制游戏所有素材
            g.draw(paddle, ball, blockList, score)
          }
        }, 1000/g.fps)
      }
      /**
       * 初始化函数
       */
      init () {
        let g = this,
            paddle = g.main.paddle,
            ball = g.main.ball,
            blockList = g.main.blockList,
            score = g.main.score
        // 设置键盘按下及松开相关注册函数
        window.addEventListener('keydown', function (event) {
         g.keydowns[event.keyCode] = true
        })
        window.addEventListener('keyup', function (event) {
          g.keydowns[event.keyCode] = false
        })
        g.registerAction = function (key, callback) {
          g.actions[key] = callback
        }
        // 注册左方向键移动事件
        g.registerAction('37', function(){
          // 判断游戏是否处于运行阶段
          if (g.state === g.state_RUNNING && paddle.isLeftMove) {
            paddle.moveLeft()
          }
        })
        // 注册右方向键移动事件
        g.registerAction('39', function(){
          // 判断游戏是否处于运行阶段
          if (g.state === g.state_RUNNING && paddle.isRightMove) {
            paddle.moveRight()
          }
        })
        window.addEventListener('keydown', function (event) {
          switch (event.keyCode) {
            // 注册空格键发射事件
            case 32 :
              if (g.state === g.state_GAMEOVER) { // 游戏结束时
                // 开始游戏
                g.state = g.state_START
                // 初始化
                g.main.start()
              } else { 
                // 开始游戏
                ball.fired = true
                g.state = g.state_RUNNING
              }
              break
            // N 键进入下一关卡
            case 78 :
              // 游戏状态为通关,且不为最终关卡时
              if (g.state === g.state_UPDATE && g.main.LV !== g.main.MAXLV) { // 进入下一关
                // 开始游戏
                g.state = g.state_START
                // 初始化下一关卡
                g.main.start(++g.main.LV)
              }
              break
            // P 键暂停游戏事件
            case 80 :
              g.state = g.state_STOP
              break
          }
        })
        // 设置轮询定时器
        g.setTimer(paddle, ball, blockList, score)
      }
    }
    

    后记:很多代码没有看懂,珍惜时间,好好学习哇~

  • 相关阅读:
    JavaScript 以POST方式打开新页面
    C# 实现守护进程
    SQL Server 之 使用RowCount遍历表数据
    SQL Server 之 存储过程调用C#编写的dll文件
    C# 多线程学习整理
    java 学习的有用链接
    git 操作命令
    关于各种Map的那些事
    JAVA 反射机制详解
    深入理解java:注解(Anotation)自定义注解
  • 原文地址:https://www.cnblogs.com/smart-girl/p/11447382.html
Copyright © 2011-2022 走看看