微信小游戏实践

最近基于微信开发的H5小游戏越来越多,微信上每天都有各种小游戏的分享。于是我也初次浅尝,在微信上实现flappy bird这个曾经火爆大江南北的H5小游戏。 效果图如下:

效果图1-1

这是一款简单的小游戏,鸟儿会在重力的作用下,往下掉落。玩家需要点击屏幕,让鸟儿飞起并顺利的穿过管子才能得分,如果鸟儿撞击管子或者落地,则游戏结束。

从上面一小段简单的描述我们了解实现这个游戏的几个点:

  • 点击屏幕让自由坠落的鸟飞起
  • 顺利穿过管道得分
  • 小鸟撞击管道或落地游戏结束

根据这几点和效果,我们来实现这个游戏,项目结构如下:
直接看代码,点这里

│  ├── base                                   // 定义游戏开发基础类
│  │   ├── animatoin.js                       // 帧动画的简易实现
│  │   ├── pool.js                            // 对象池的简易实现
│  │   └── sprite.js                          // 游戏基本元素精灵类
│  ├── libs
│  │   ├── symbol.js                          // ES6 Symbol简易兼容
│  │   └── weapp-adapter.js                   // 小游戏适配器
│  ├── npc
│  │   └── enemy.js                           // 初始化管道和控制
│  |   └── pipe.js                            // 管道类
│  ├── player
│  │   └── bird.js                           // 小鸟类
│  ├── runtime
│  │   ├── background.js                      // 背景类
│  │   ├── gameinfo.js                        // 用于展示分数和结算界面
│  |   ├── floor.js                           // 地板类
│  │   └── music.js                           // 全局音效管理器
│  ├── databus.js                             // 管控游戏状态
│  └── main.js                                // 游戏入口主函数
├── game.js                                   // 游戏入口
├── game.json
├── project.config.json

项目结构就是在官方给的Demo上进行修改的,这里就不做具体的介绍。请看官方文档,下面主要介绍一下enemy.js的实现,其他类都比较简单。

enemy.js实现
import DataBus from '../databus'
import Pipe from './pipe'
import Music from '../runtime/music'

const databus = new DataBus()

const screenWidth = window.innerWidth
const screenHeight = window.innerHeight

const pipeH = databus.pipeInfo.height;
const pipeW = databus.pipeInfo.width;

const constant = pipeH + databus.gap;

function rnd(start, end) {
  return Math.floor(Math.random() * (end - start) + start)
}

export default class Enemy {

  constructor() {
    this.createdRndPipe();
    this.music = new Music();
  }

  setBird(bird) {
    this.bird = bird;
  }

  renderPipe(ctx) {
    
    let pipeLen = databus.pipe.length;

    for (let i = 0; i < pipeLen; i++) {

      const currPipeX = databus.pipe[i].x
      const currPipeY = databus.pipe[i].y
      
      this.pipeNorth = new Pipe('images/pipeNorth.png', currPipeX, currPipeY, ctx);
      this.pipeNorth.drawToCanvas(ctx);

      this.pipeSouth = new Pipe('images/pipeSouth.png', currPipeX, currPipeY + constant, ctx);
      this.pipeSouth.drawToCanvas(ctx);
    
      databus.pipe[i].x -= 1;

      if (databus.pipe[i].x == 12) {
        this.createdRndPipe();
      }
      
      this.collisionDetection(databus.pipe[i]);
      
      this.scorPlay(databus.pipe[i]);
    }
  }

  /**
   * 生产一个随机的管道
  */
  createdRndPipe() {
    const rndX = rnd(pipeH * 0.5, pipeH * 0.9) - pipeH
    databus.pipe.push({
      x: screenWidth,
      y: rndX
    });
  }
  
  /**
   * 分数打印
   * pipe => 当前pipe[i]
  */
  scorPlay(pipe) {
    if (pipe.x === 5) {
      databus.score += 1;
      this.music.playScore();
    }
  }

  /**
   * 检测是否碰撞pipe
  */
  collisionDetection(pipe) {
    const bird = this.bird;
    const bX = bird.x;
    const bY = bird.y;
    const bW = bird.width;
    const bH = bird.height;

    const floorH = 118;

    if (bX + bW >= pipe.x && bX <= pipe.x + pipeW && (bY <= pipe.y + pipeH || bY + bH >= pipe.y + constant) ||
      bY + bH >= screenHeight - floorH
    ) {
      databus.gameOver = true;
      this.music.playGameOver();
    }
  }
}

enemy.js类主包含管道的移动、新管道的生成、鸟和管道的碰撞检测。

renderPipe主要是用来渲染管道的,我们在初始化databus.pipe数据容器的时候,会预先插入一个管道信息。然后通过遍历databus.pipe来绘制管道。

  1. 移动: 通过给每个管道的 x 坐标 -1实现移动
  2. 生产新管道:当有管道快走出屏幕时,生产一个新的管道插入到databus.pipe里面。
  3. 碰撞检测:
    • bX + bW >= pipe.x && bX <= pipe.x + pipeW 判断鸟在如下图<--->的位置。
    • 在满足上一条的情况下,并且满足(bY <= pipe.y + pipeH || bY + bH >= pipe.y + constant)就可以判断鸟在(1)或(2)的位置,这种情况为游戏结束。
    • 还有一种结束的情况是bY + bH >= screenHeight - floorH 就是鸟落地的情况。
      WechatIMG15.jpg
总结

H5游戏应该说是我的初次实践,第一感觉游戏主要是在canvas上进行元素绘制,然后各种操作元素的X、Y坐标和检测他们坐标之间的关系,当然这只是对简单的游戏而言。表面看起来和其他项目有很大的不同,其实都在写JavaScript。

你可能感兴趣的:(微信小游戏实践)