效果图1-1最近基于微信开发的H5小游戏越来越多,微信上每天都有各种小游戏的分享。于是我也初次浅尝,在微信上实现flappy bird这个曾经火爆大江南北的H5小游戏。 效果图如下:
这是一款简单的小游戏,鸟儿会在重力的作用下,往下掉落。玩家需要点击屏幕,让鸟儿飞起并顺利的穿过管子才能得分,如果鸟儿撞击管子或者落地,则游戏结束。
从上面一小段简单的描述我们了解实现这个游戏的几个点:
- 点击屏幕让自由坠落的鸟飞起
- 顺利穿过管道得分
- 小鸟撞击管道或落地游戏结束
根据这几点和效果,我们来实现这个游戏,项目结构如下:
直接看代码,点这里
│ ├── 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来绘制管道。
- 移动: 通过给每个管道的 x 坐标 -1实现移动
- 生产新管道:当有管道快走出屏幕时,生产一个新的管道插入到databus.pipe里面。
- 碰撞检测:
-
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。
网友评论