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"這樣去填寫圖片路徑