1. 目录结构:
2. game.js:入口文件
//game.js文件完整代码:
import Main from "./src/mian.js"
new Main()
3. game.json:全家配置参数文件,参考https://developers.weixin.qq.com/minigame/dev/reference/configuration/app.html。
{
"deviceOrientation":"portrait",
" showStatusBar":true
}
4. src/main.js:初始化js逻辑代码文件:
import Background from "./all/background.js"
import Audio from "./all/audio.js"
import Hero from "./all/hero.js"
import Bullet from "./all/bullet.js"
import Enemy from "./all/enemy.js"
import Animation from "./all/animation.js"
import Text from "./all/text.js"
class main{
constructor(){
this.canvas = wx.createCanvas()
this.ctx = this.canvas.getContext('2d')
this.ch=GameGlobal.innerHeight //屏幕的高
this.cw = GameGlobal.innerWidth //屏幕的宽
this.bulletObjArr=[] //存放屏幕内可见的子弹数组
this.enemyObjArr=[] //存放屏幕内可见的敌机数组
this.animationObjArr=[] //存放屏幕内可见的爆炸效果动画的数组
this.bangImgArr=[] //提前缓存19张爆炸效果图数组
for(let i=0;i<19;i++){
let obj={
bang:wx.createImage()
}
obj.bang.src = `./images/explosion${i+1}.png`
this.bangImgArr.push(obj)
}
this.scoreNum=0 //得分
this.bgMoveTop=0 //背景图片移动的上下位移
this.initStart() //初始化逻辑
}
initStart(){
let bgObj=new Background(this.ctx) //实例化背景图
new Audio() //实例化背景音乐
let heroObj=new Hero(this.ctx) //实例化英雄飞机
let enemyObj = null
var bulletObj = null
setInterval(()=>{ //定时器,每隔300ms,绘制一个子弹 并存放在子弹数组中
bulletObj = new Bullet(this.ctx, heroObj)
this.bulletObjArr.push(bulletObj)
},300)
setInterval(() => { //定时器,每隔300ms,绘制一个敌机 并存放在敌机数组中
enemyObj = new Enemy(this.ctx)
this.enemyObjArr.push(enemyObj)
},1000)
let textObj = new Text(this.ctx) //实例化得分和结束游戏弹窗
this.render(bgObj, heroObj, textObj) //开始绘制,递归函数
}
render(bgObj, heroObj, textObj){
this.bgMoveTop++
this.bgMoveTop=this.bgMoveTop > this.ch ? 0 : this.bgMoveTop
requestAnimationFrame(()=>{
this.ctx.clearRect(0, 0, this.cw, this.ch) //清空画布
bgObj.move(this.bgMoveTop) //绘制背景
this.bulletObjArr = this.bulletObjArr.filter(item=>item.isShow) //过滤掉超出屏幕的子弹
this.bulletObjArr.forEach((item)=>{ //绘制连续的子弹
item.draw() //先绘制子弹再绘制飞机
})
textObj.scoreDraw(this.scoreNum) //绘制分数
heroObj.draw() //绘制英雄飞机
this.enemyObjArr = this.enemyObjArr.filter(item => item.isShow) //过滤掉超出屏幕的子弹
this.enemyObjArr.forEach((item)=>{
heroObj.isBang(item) //检测敌机和英雄飞机是否相撞
item.draw() //绘制敌机
for (var i = 1; i < this.bulletObjArr.length;i++){
let bool=item.isBang(this.bulletObjArr[i])
if(bool){ //击中敌机
this.scoreNum++
this.bulletObjArr[i]=false
let animate = new Animation(this.ctx, item.x, item.y) //绘制爆炸效果
this.animationObjArr.push(animate)
}
}
})
this.animationObjArr = this.animationObjArr.filter(item => item.isShow) //绘制爆炸效果
this.animationObjArr.forEach((item) => { //绘制爆炸效果
item.draw(this.bangImgArr) //绘制爆炸效果
})
if (!heroObj.isGameOver){ //游戏结束,就停止绘制
this.render(bgObj, heroObj, textObj)
}else{ //游戏结束 绘制结束时弹出
textObj.popup(this.scoreNum)
}
})
}
}
export default main
5. src/all/background.js:绘制背景图
export default function(ctx){
let width= GameGlobal.innerWidth,
height= GameGlobal.innerHeight
let obj={
bg: wx.createImage(),
width: width,
height: height,
move:function(top){
ctx.drawImage(this.bg, 0, 0, this.bg.width,this.bg.height, 0, top ,this.width,this.height)
ctx.drawImage(this.bg, 0, 0, this.bg.width, this.bg.height, 0, top - this.height, this.width, this.height)
}
}
obj.bg.src='images/bg.jpg'
obj.bg.width=512
obj.bg.height=512
return obj
}
6. src/all/hero.js:绘制英雄飞机
export default function (ctx) {
let width = GameGlobal.innerWidth,
height = GameGlobal.innerHeight
let obj = {
newHero: wx.createImage(),
x:0,
y:0,
imgW:80,
imgH:80,
isGameOver:false, //游戏是否结束,即结束时停止渲染画图
draw: function () {
ctx.drawImage(this.newHero, 0, 0, this.newHero.width, this.newHero.height, this.x, this.y, this.imgW, this.imgH)
},
isBang:function(enemy){
let cX=enemy.x+enemy.imgW/2
let cY = enemy.y + enemy.imgH / 2
if(cX>this.x && cX<this.x+this.imgW && cY>this.y && cY<this.y+this.imgH){
// console.log("飞机和影响飞机相撞了,game over")
this.isGameOver=true
}
}
}
obj.newHero.src = 'images/hero.png'
obj.newHero.width = 186
obj.newHero.height = 130
obj.x = width / 2 - obj.imgW/2
obj.y = height - obj.imgH -30
let isMove = false
wx.onTouchStart((e) => {
let touch = e.changedTouches[0] //获取手指按下的对象
let touX = touch.clientX
let touY = touch.clientY
if (touX > obj.x && touX < obj.x + obj.newHero.width / 2 && touY > obj.y && touY < obj.y + obj.newHero.height / 2) {
isMove = true
}
})
wx.onTouchMove((e) => {
let touch = e.changedTouches[0] //获取手指按下的对象
let touX = touch.clientX
let touY = touch.clientY
if (isMove) {
let x = touX - obj.imgW / 2
let y = touY - obj.imgH / 2
x=x<0?0:x //限制飞机可以拖拽的边界范围
x = x > width - obj.imgW ? width - obj.imgW:x
y=y<0?0:y
y = y > height - obj.imgH ? height - obj.imgH : y
obj.x = x
obj.y = y
}
})
wx.onTouchEnd((e) => {
isMove = false
})
return obj
}
7. src/all/bullet.js:绘制子弹
export default function (ctx, heroObj){
let width = GameGlobal.innerWidth,
height = GameGlobal.innerHeight
let obj={
newBullet: wx.createImage(),
x: 0,
y: 0,
imgW: 16,
isShow:true, //是否超出屏幕显示
imgH: 30,
draw: function () {
this.y-=5
if (this.y< -30){ //判断子弹是否飞出屏幕
this.isShow=false
}
ctx.drawImage(this.newBullet, 0, 0, this.newBullet.width, this.newBullet.height, this.x, this.y, this.imgW, this.imgH)
}
}
obj.newBullet.src = 'images/bullet.png'
obj.newBullet.width = 62
obj.newBullet.height = 108
obj.x = heroObj.x + heroObj.imgW / 2 - obj.imgW/2
obj.y = heroObj.y+10
let biu = wx.createInnerAudioContext() //发射子弹的声音
biu.src="audios/bullet.mp3"
biu.play()
return obj
}
8. src/all/enemy.js:绘制敌机
export default function(ctx){
let width = GameGlobal.innerWidth,
height = GameGlobal.innerHeight
let obj={
enemy: wx.createImage(),
x:0,
y:-60,
imgW:60,
imgH:60,
isShow:true, //当敌机溢出屏幕时,隐藏
draw:function(){
this.y = this.y+5
if (this.y > height + this.imgH){
this.isShow = false
}
ctx.drawImage(this.enemy, 0, 0, this.enemy.width, this.enemy.height,this.x,this.y,this.imgW,this.imgH)
},
isBang:function(bullet){ //敌机是否与子弹碰撞到
var cX = bullet.x + bullet.imgW/2
var cY = bullet.y + bullet.imgH / 2
if (cX > this.x && cX<this.x+this.imgW && cY>this.y && cY<this.y+this.imgH && this.y>30){
// console.log("子弹在屏幕内打中敌机了")
this.isShow=false
return true
}
}
}
obj.enemy.src ="images/enemy.png"
obj.enemy.width=120
obj.enemy.height=79
obj.x=Math.random()*(width-obj.imgW)
return obj
}
9. src/all/animation.js:绘制爆炸效果
export default function(ctx,dx,dy){
let width = GameGlobal.innerWidth,
height = GameGlobal.innerHeight
let obj={
num:0,
isShow:true,
draw: function (bangImgArr){
this.num++
if(this.num>18){
this.num=18
this.isShow=false
}
ctx.drawImage(bangImgArr[this.num].bang,0,0,64,48,dx,dy,60,60)
}
}
// let boom = wx.createInnerAudioContext() //爆炸声音
// boom.src = "audios/boom.mp3"
// boom.play()
return obj
}
10. src/all/text.js:绘制得分以及游戏结束弹窗
export default function(ctx){
let width = GameGlobal.innerWidth,
height = GameGlobal.innerHeight
let obj={
img:wx.createImage(),
btnImg:wx.createImage(),
scoreDraw:function(score){ //绘制分数
ctx.font='20px arial'
ctx.fillStyle='#fff'
ctx.fillText(score,10,40)
},
popup: function (score){ //分别是绘制背景弹出和重新开始的按钮背景图
ctx.drawImage(this.img,0,0,119,108,width/2-150,height/2-100,300,300)
ctx.fillText('游戏结束', width/2-40, height/2-100+50)
ctx.fillText(`得分:${score}`, width / 2 - 40, height / 2 - 100+130)
ctx.drawImage(this.img, 120, 6, 39, 24, width / 2 - 60, height / 2 - 100+180, 120, 40)
ctx.fillText('重新开始', width / 2 - 40, height / 2 - 100+205)
}
}
obj.img.src ="images/Common.png"
return obj
}
11. src/all/audio.js:绘制背景音乐
class audio {
constructor() {
this.newAudio = wx.createInnerAudioContext()
this.newAudio.src='audios/bgm.mp3'
this.newAudio.play()
}
}
export default audio
备注:
1. 创建game.json全局配置:参考https://developers.weixin.qq.com/minigame/dev/reference/configuration/app.html。
全局配置参数:
1.1 deviceOrientation:屏幕选择方向,包括竖屏('portrait')和横屏('landscape')。
1.2 showStatusBar:是否显示手机顶部电量,信号灯状态栏,默认为false,只有在竖屏下才能显示状态栏。
2. wx.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh):作为参数。dx和dy是image在canvas中定位的坐标值;dw和dh是image在canvas中即将绘制区域(相对dx和dy坐标的偏移量)的宽度和高度值;sx和sy是image所要绘制的起始位置,sw和sh是image所要绘制区域(相对image的sx和sy坐标的偏移量)的宽度和高度值。
3. img.src="./images/xxx.png"的图片路径,在真机上显示不出来,需要img.src="images/xxx.png"这样去填写图片路径