欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

原生js做h5小游戏之打砖块

程序员文章站 2024-03-18 16:31:22
...

前言

首先,先说明一下做这个系列的目的:其实主要源于博主希望熟练使用 canvas 的相关 api ,同时对小游戏的实现逻辑比较感兴趣,所以希望通过这一系列的小游戏来提升自身编程能力;关于 es6 语法,个人认为以后 es6 语法会越来越普及,所以算是提前熟悉语法使用技巧。小游戏的实现逻辑上可能并不完善,也许会有一些 bug ,但是毕竟只是为了提升编程能力与技巧,希望大家不要太较真

作为第一次分享,我选择打砖块这个逻辑不算太复杂的小游戏。同时,为了接近真实游戏效果,在游戏中也添加了关卡,砖块血量,以及物理碰撞模型的简略实现。其实关注游戏实现逻辑就好了

线上演示地址:https://yangyunhe369.github.io/h5-game-blockBreaker/
github地址:https://github.com/yangyunhe369/h5-game-blockBreaker
ps:github地址有代码演示,以及源码可供参考,线上演示地址可供预览

先上一个游戏完成后的截图
原生js做h5小游戏之打砖块

游戏工程目录如下

.
├─ index.html                   // 首页html
│  
├─ css                          // css样式资源文件
├─ images                       // 图片资源文件  
└─ js
   ├─ common.js                 // 公共js方法
   ├─ game.js                   // 游戏主要运行逻辑
   └─ scene.js                  // 游戏场景相关类

游戏实现逻辑

这里对游戏中需要绘制的挡板、小球、砖块、计分板都进行了实例化,并将游戏主要运行逻辑单独进行实例化

挡板 Paddle

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 () {
    ...
  }
  // 向右移动
  moveRight () {
    ...
  }
  // 小球、挡板碰撞检测
  collide (ball) {
    ...
  }
  // 计算小球、挡板碰撞后x轴速度值
  collideRange (ball) {
    ...
  }
}

挡板类:主要会定义其坐标位置、图片大小、x 轴位移速度、对小球反弹速度的控制等,再根据不同按键响应 moveLeft 和 moveRight 移动事件,collide 方法检测小球与挡板是否碰撞,并返回布尔值

小球 Ball

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) {
    ...
  }
}

小球类:其大部分属性与挡板类似,主要通过 move 方法控制小球运动轨迹

砖块 Block

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 () {
    ...
  }
  // 小球、砖块碰撞检测
  collide (ball) {
    ...
  }
  // 计算小球、砖块碰撞后x轴速度方向
  collideBlockHorn (ball) {
    ...
  }
}

砖块类:会有两个属性不同,分别是 life 和 是否存活。然后,在小球和砖块撞击时,调用 kill 方法扣除当前砖块血量,当血量为0时,清除砖块。collide 方法检测小球与砖块是否碰撞,并返回布尔值

计分板 Score

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 () {
    ...
  }
}

分数类:会记录当前分数、关卡数,其中 computeScore 方法会在小球碰撞砖块且砖块血量为0时调用,并累加总分

场景 Scene

class Scene {
  constructor (lv) {
    let s = {
      lv: lv,                                         // 游戏难度级别
      canvas: document.getElementById("canvas"),      // canvas 对象
      blockList: [],                                  // 砖块坐标集合
    }
    Object.assign(this, s)
  }
  // 实例化所有砖块对象
  initBlockList () {
    ...
  }
  // 创建砖块坐标二维数组,并生成不同关卡
  creatBlockList () {
    ...
  }
}

场景类:主要是根据游戏难度级别,绘制不同关卡及砖块集合(目前只生成了三个关卡)。其中 creatBlockList 方法先生成所有砖块的二维坐标数组,再调用 initBlockList 方法进行所有砖块的实例化

游戏主逻辑 Game

class Game {
  constructor (fps = 60) {
    let g = {
      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: fps,                                                     // 动画帧数,默认60
    }
    Object.assign(this, g)
  }
  ...
}

游戏核心类:这里包括游戏主要运行逻辑,包括但不限于以下几点


  • 绘制游戏整个场景

  • 调用定时器逐帧绘制动画

  • 游戏通关及游戏结束判定

  • 绑定按钮事件

  • 边界检测处理函数

  • 碰撞检测处理函数

入口函数 _main

let _main = {
  LV: 1,                               // 初始关卡
  MAXLV: 3,                            // 最终关卡
  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.fps)
    /**
     * 游戏初始化
     */
    self.game.init(self)
  }
}

入口函数:实现了游戏中需要的所有类的实例化,并进行游戏的初始化

作者: 弦云孤赫 原文链接: http://www.yangyunhe.me/2017/h5-game-blockBreaker/
本文可以转载,但务必注明原作者和原出处。
ps:博主在本次项目中使用了大量 es6 语法,故对于 es6 语法不太熟悉的小伙伴最好能先了解一些基本的原理再继续阅读。


更多专业前端知识,请上【猿2048】www.mk2048.com