微信小游戏 demo 飞机大战 代码分析 (一)(game.js, main.js)
微信小游戏 demo 飞机大战 代码分析(一)(main.js)
微信小游戏 demo 飞机大战 代码分析(二)(databus.js)
微信小游戏 demo 飞机大战 代码分析(三)(spirit.js, animation.js)
微信小游戏 demo 飞机大战 代码分析(四)(enemy.js, bullet.js, index.js)
本博客将使用逐行代码分析的方式讲解该demo,本文适用于对其他高级语言熟悉,对js还未深入了解的同学,博主会尽可能将所有遇到的不明白的部分标注清楚,若有不正确或不清楚的地方,欢迎在评论中指正
本文的代码均由微信小游戏自动生成的demo飞机大战中获取
文件目录
game.js
首先让我们来看一下作为入口的game.js,可以看到在这里只进行了main类的初始化,因此下一步我们应该查看一下main类中的函数
代码
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
)
}
}
一点基础知识
- 帧:游戏中的帧和动画中的帧,视频中的帧概念类似,即游戏过程中物体和动画效果变化的一个周期。
- 精灵:是游戏中的一个基本概念,指的是在游戏中的一个基本物体或动画或贴图,如NPC或者敌人,在本例中有子弹,敌机和玩家
- 回调函数:在特定事件发生后,由事件方进行调用的函数
- 画布:顾名思义就是使用了画东西的地方,其实就是用于渲染相关内容的位置
main.js
main 即为游戏的主函数,我们来逐个分析一下其内容
- export default 为 ES6,即js的一个版本中的语言,在js中,任何类或对象使用export既可以在其他文件中通过import进行调用使用,使用 import {类或对象名} from 文件路径,但若使用export default则可以省略 { }, 但一份文件中仅仅可以存在一个export default
初始化生成对象
在main函数前其调用生成了一个2d画布,名称为ctx
生成了一个数据总线对象databus,数据总线的内容将在下次博客中解释
main 类
contructor()
contructor 用于创建main 对象,其中调用了restart函数,因此我们跳转到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
那么该函数所属的类就丢失了,那么该函数一些执行也就无法进行 - 为了避免这样的情况,js使用bind函数,将所需的类绑定到该函数上,这样就有效地解决了这个问题
- 例如在该代码中,若写作
window.requestAnimationFrame()
- 该函数使用了两个参数,第一个是回调函数,第二个是画布
- 画布的功能即用来工作的区域
- 而回调函数的作用是在浏览器在该帧渲染完毕之后,调用的函数,根据博主的资料查询,回调函数执行次数通常是每秒60次,但在大多数遵循W3C建议的浏览器中,回调函数执行次数通常与浏览器屏幕刷新次数相匹配。
- 在该例子中,restart中的该函数仅仅是使用初始化的main对象更新loop函数,并将其作为刷新内容
- 但由于main对象中的逻辑会产生变更,因此在之后的loop函数也对其进行了请求,并绑定了参数。使用新缠身过的main对象和新产生的canvas在浏览器中进行渲染
enemyGenerate()
该函数用于生成敌人飞机
- 在databus中有一个frame参数,相当于每次刷新(更新)的计数器,
- 使用该函数时,若刷新次数为30的整数倍时,就会申请一个新的敌机对象并初始化,其中init的参数为该敌机的速度,生成后加入databus对象的存储数组中
collisionDetection()
全局碰撞检测
- 首先对于每个子弹,判断子弹是否与敌机相撞,若相撞则隐藏敌机和子弹
- 该处需要解释一下的是,将子弹和敌机隐藏的是直接代表子弹和敌机已经销毁
- 但此处并未在逻辑中将对象销毁,而是在绘图中判断其visible是否为true,若为true则才会画入画布中
- 而统一更新回收入pool
- 对每一架敌机,判断是否与用户相撞,若相撞,则在databus中设置游戏结束
touchEventHandler(e)
游戏结束后判断是否重新开始的函数
- 获取触摸的坐标
- 在gameinfo中获取重新开始上下左右xy坐标
- 比对触摸位置是否在按钮内部,若在则调用restart函数重新启动函数
render()
渲染函数,用于渲染场景,用于每次修改内容后重新渲染场景内容(每一帧调用)
- 清除画布的所有内容
- 调用背景类的渲染函数,在ctx上渲染出一个背景
- concat函数为js函数,用于连接连个数组
- 连接databus中的bullets和enemys数组,并且将这个合成数组中的每一项画到画布上,画到画布上的操作是以利用函数drawToCanvas,而该函数实现于Spirite类中,
- spirit即精灵,是游戏设计中的一个概念,相当于游戏中一个最基本的物体或者一个概念,该demo中的spirit实现方式将在后续博客中写上
- 将player画到画布上,同样的,player也继承于Spirit类
- 将所有动画类的未播放的内容进行播放,在该demo中,Animation类继承Spirit,而所有物体均继承于Animation类,因此都具有该能力,不过由于所有物体都均仅有一帧图像,因此无需进行播放,
- 在databus类中有一个专门存放动画的数组,任何继承于Animation类的对象都会在初始化构造时被放入该数组当中
- 调用gameinfo的函数更新图像左上角的分数内容
- 判断,若游戏结束
- 若未绑定事件,将touchHandler事件添加绑定,
- 将事件加入监听中
- (该段代码博主并未非常理解,欢迎在评论中指正或指导)
update()
游戏逻辑更新主函数
- 若游戏已经结束,不执行该代码,直接放回结束
- 更新背景参数
- 对所有bullets和enemys对象进行更新
- 调用enemyGenerate() 生成敌人(根据前面描述,需要判断是否满足刚好经过30帧)
- 进行全局碰撞检测,并进行处理
- 判断是否经过20帧,每经过20帧,调用player生成一个新的bullet(子弹),并且调用射击音乐
loop()
实现游戏帧循环
- 每次循环将帧计数器加一
- 更新逻辑
- 渲染逻辑更新后的场景
- 使用
window.requestAnimationFrame
进行调用,为下一帧界面渲染做准备
微信小游戏 demo 飞机大战 代码分析 (一)(game.js, main.js)的更多相关文章
- 微信小游戏 demo 飞机大战 代码分析(四)(enemy.js, bullet.js, index.js)
微信小游戏 demo 飞机大战 代码分析(四)(enemy.js, bullet.js, index.js) 微信小游戏 demo 飞机大战 代码分析(一)(main.js) 微信小游戏 demo 飞 ...
- 微信小游戏 demo 飞机大战 代码分析 (三)(spirit.js, animation.js)
微信小游戏 demo 飞机大战 代码分析(三)(spirit.js, animation.js) 微信小游戏 demo 飞机大战 代码分析(一)(main.js) 微信小游戏 demo 飞机大战 代码 ...
- 微信小游戏 demo 飞机大战 代码分析 (二)(databus.js)
微信小游戏 demo 飞机大战 代码分析(二)(databus.js) 微信小游戏 demo 飞机大战 代码分析(一)(main.js) 微信小游戏 demo 飞机大战 代码分析(三)(spirit. ...
- Python小游戏之 - 飞机大战美女 !
用Python写的"飞机大战美女"小游戏 源代码如下: # coding=utf-8 import os import random import pygame # 用一个常量来存 ...
- Python小游戏之 - 飞机大战 !
用Python写的"飞机大战"小游戏 源代码如下: # coding=utf-8 import random import os import pygame # 用一个常量来存储屏 ...
- 微信demo小游戏:飞机大战从无到有
微信demo游戏飞机大战从无到有 现在创建新项目会默认给飞机大战的demo,这里给大家从基础开始讲解游戏的从无到有是怎么实现的. 具体实现步骤: 创建背景图->背景图运动起来->创建飞机并 ...
- 【转】利用 three.js 开发微信小游戏的尝试
前言 这是一次利用 three.js 开发微信小游戏的尝试,并不能算作是教程,只能算是一篇笔记吧. 微信 WeChat 6.6.1 开始引入了微信小游戏,初期上线了一批质量相当不错的小游戏.我在查阅各 ...
- 微信小游戏的本地缓存和清除的使用 (text.js image.js file-util.js)
参考: 微信小游戏,文件系统 UpdateManager-小游戏 一.Egret提供的本地缓存工具类( 备注:新版本进行了修改,并增加了sound.js等) 在微信小游戏项目中,Egret提供了fil ...
- cocos creator开发微信小游戏记录
先用cocoscreator实现游戏逻辑 在cocoscreator项目里可以调用微信小游戏api 在cocos里面判断小游戏的运行环境 if (cc.sys.platform === cc.sys. ...
随机推荐
- BZOJ4552(二分+线段树)
要点 序列是n个不同的数,则新学到的一种策略就是二分这个位置的答案,然后可以上下调. 神奇地只关注大于还是小于mid并赋值0.1,这样m个操作的排序就能用线段树维护了! #include <cs ...
- js——本地存储
1. cookie 容量小:4k,在同源的http请求时携带传输,占用带宽,有日期限制 <!DOCTYPE html> <html lang="en"> & ...
- 转 MYSQL_GTID详解
http://blog.itpub.net/27067062/viewspace-2141906/ 一.GTID概述 GTID是MYSQL5.6新增的特性,GTID(Global Transac ...
- VMware ESXi5忘记登录密码解决办法
很久没有登录ESXi5了,今天登录发现忘记密码了: 网上搜索到的方法都是使用linux其他版本的镜像的恢复模式来重置密码(尝试过了,ESXI自己的镜像没有恢复模式).于是使用现有的Ubuntu镜像来操 ...
- 数据库(DBUtils)
DBUtils和连接池 今日内容介绍 u DBUtils u 连接池 第1章 DBUtils 如果只使用JDBC进行开发,我们会发现冗余代码过多,为了简化JDBC开发,本案例我们讲采用apache c ...
- jQuery学习笔记(三)
jQuery中的事件 页面加载 原生DOM中的事件具有页面加载的内容onload事件,在jQuery中同样提供了对应的内容ready()函数. ready与onload之间的区别: onload re ...
- 常用模块random,time,os,sys,序列化模块
一丶random模块 取随机数的模块 #导入random模块 import random #取随机小数: r = random.random() #取大于零且小于一之间的小数 print(r) #0. ...
- vue $set用法
需求,想给下面的数据添加一个hoby属性 {{data.hoby}}-->让这里的视图改变 data:{ name: "简书", age: '3', info: { cont ...
- testNG测试基础一
1.TestNG概念 TestNG:Testing Next Generation 下一代测试技术,是一套根据JUnit和Nunit思想构建的利用注释来强化测试功能的测试框架,可用来做单元测试,也可用 ...
- UVA Stacks of Flapjacks 栈排序
题意:给一个整数序列,输出每次反转的位置,输出0代表排序完成.给一个序列1 2 3 4 5,这5就是栈底,1是顶,底到顶的位置是从1~5,每次反转是指从左数第i个位置,将其及其左边所有的数字都反转,假 ...