微信小游戏 demo 飞机大战 代码分析(二)(databus.js)html
微信小游戏 demo 飞机大战 代码分析(三)(spirit.js, animation.js)canvas
微信小游戏 demo 飞机大战 代码分析(四)(enemy.js, bullet.js, index.js)数组
本博客将使用逐行代码分析的方式讲解该demo,本文适用于对其余高级语言熟悉,对js还未深刻了解的同窗,博主会尽量将全部遇到的不明白的部分标注清楚,如有不正确或不清楚的地方,欢迎在评论中指正浏览器
本文的代码均由微信小游戏自动生成的demo飞机大战中获取微信
首先让咱们来看一下做为入口的game.js,能够看到在这里只进行了main类的初始化,所以下一步咱们应该查看一下main类中的函数ide
代码函数
import Player from './player/index' import Enemy from './npc/enemy' import BackGround from './runtime/background' import GameInfo from './runtime/gameinfo' import Music from './runtime/music' import DataBus from './databus' let ctx = canvas.getContext('2d') let databus = new DataBus() /** * 游戏主函数 */ export default class Main { constructor() { // 维护当前requestAnimationFrame的id this.aniId = 0 //从新生成新的界面 this.restart() } //界面生成函数 restart() { databus.reset() canvas.removeEventListener( 'touchstart', this.touchHandler ) this.bg = new BackGround(ctx) this.player = new Player(ctx) this.gameinfo = new GameInfo() this.music = new Music() this.bindLoop = this.loop.bind(this) this.hasEventBind = false // 清除上一局的动画 window.cancelAnimationFrame(this.aniId); this.aniId = window.requestAnimationFrame( this.bindLoop, canvas ) } /** * 随着帧数变化的敌机生成逻辑 * 帧数取模定义成生成的频率 */ enemyGenerate() { if ( databus.frame % 30 === 0 ) { let enemy = databus.pool.getItemByClass('enemy', Enemy) enemy.init(6) databus.enemys.push(enemy) } } // 全局碰撞检测 collisionDetection() { let that = this databus.bullets.forEach((bullet) => { for ( let i = 0, il = databus.enemys.length; i < il;i++ ) { let enemy = databus.enemys[i] if ( !enemy.isPlaying && enemy.isCollideWith(bullet) ) { enemy.playAnimation() that.music.playExplosion() bullet.visible = false databus.score += 1 break } } }) for ( let i = 0, il = databus.enemys.length; i < il;i++ ) { let enemy = databus.enemys[i] if ( this.player.isCollideWith(enemy) ) { databus.gameOver = true break } } } // 游戏结束后的触摸事件处理逻辑 touchEventHandler(e) { e.preventDefault() let x = e.touches[0].clientX let y = e.touches[0].clientY let area = this.gameinfo.btnArea if ( x >= area.startX && x <= area.endX && y >= area.startY && y <= area.endY ) this.restart() } /** * canvas重绘函数 * 每一帧从新绘制全部的须要展现的元素 */ render() { ctx.clearRect(0, 0, canvas.width, canvas.height) this.bg.render(ctx) databus.bullets .concat(databus.enemys) .forEach((item) => { item.drawToCanvas(ctx) }) this.player.drawToCanvas(ctx) databus.animations.forEach((ani) => { if ( ani.isPlaying ) { ani.aniRender(ctx) } }) this.gameinfo.renderGameScore(ctx, databus.score) // 游戏结束中止帧循环 if ( databus.gameOver ) { this.gameinfo.renderGameOver(ctx, databus.score) if ( !this.hasEventBind ) { this.hasEventBind = true this.touchHandler = this.touchEventHandler.bind(this) canvas.addEventListener('touchstart', this.touchHandler) } } } // 游戏逻辑更新主函数 update() { if ( databus.gameOver ) return; this.bg.update() databus.bullets .concat(databus.enemys) .forEach((item) => { item.update() }) this.enemyGenerate() this.collisionDetection() if ( databus.frame % 20 === 0 ) { this.player.shoot() this.music.playShoot() } } // 实现游戏帧循环 loop() { databus.frame++ this.update() this.render() this.aniId = window.requestAnimationFrame( this.bindLoop, canvas ) } }
main 即为游戏的主函数,咱们来逐个分析一下其内容oop
在main函数前其调用生成了一个2d画布,名称为ctx动画
生成了一个数据总线对象databus,数据总线的内容将在下次博客中解释this
contructor 用于建立main 对象,其中调用了restart函数,所以咱们跳转到restart函数中进行查看
该函数用于从新生成一个界面
首先重置数据总线对象的内容
监听触碰事件
初始化背景对象,玩家对象,游戏信息对象和音乐对象
this.bg = new BackGround(ctx) this.player = new Player(ctx) this.gameinfo = new GameInfo() this.music = new Music()
绑定事件循环,初始化状态,并开始运行
this.bindLoop = this.loop.bind(this) this.hasEventBind = false // 清除上一局的动画 window.cancelAnimationFrame(this.aniId); this.aniId = window.requestAnimationFrame( this.bindLoop, canvas )
js语法中,能够将某个对象的方法单独拿出来做为一个方法使用,可是在使用过程当中,避免不了出现未知该函数所指向的对象的状况
this.bindLoop = this.loop
那么该函数所属的类就丢失了,那么该函数一些执行也就没法进行window.requestAnimationFrame()
该函数用于生成敌人飞机
全局碰撞检测
游戏结束后判断是否从新开始的函数
渲染函数,用于渲染场景,用于每次修改内容后从新渲染场景内容(每一帧调用)
游戏逻辑更新主函数
实现游戏帧循环
window.requestAnimationFrame
进行调用,为下一帧界面渲染作准备