微信小游戲 demo 飛機大戰 代碼分析(三)(spirit.js, animation.js)
微信小游戲 demo 飛機大戰 代碼分析(一)(main.js)
微信小游戲 demo 飛機大戰 代碼分析(二)(databus.js)
微信小游戲 demo 飛機大戰 代碼分析(四)(enemy.js, bullet.js, index.js)
本博客將使用逐行代碼分析的方式講解該demo,本文適用於對其他高級語言熟悉,對js還未深入了解的同學,博主會盡可能將所有遇到的不明白的部分標注清楚,若有不正確或不清楚的地方,歡迎在評論中指正
本文的代碼均由微信小游戲自動生成的demo飛機大戰中獲取
spirit.js
游戲基礎的精靈類
代碼
/**
* 游戲基礎的精靈類
*/
export default class Sprite {
constructor(imgSrc = '', width= 0, height = 0, x = 0, y = 0) {
this.img = new Image()
this.img.src = imgSrc
this.width = width
this.height = height
this.x = x
this.y = y
this.visible = true
}
/**
* 將精靈圖繪制在canvas上
*/
drawToCanvas(ctx) {
if ( !this.visible )
return
ctx.drawImage(
this.img,
this.x,
this.y,
this.width,
this.height
)
}
/**
* 簡單的碰撞檢測定義:
* 另一個精靈的中心點處於本精靈所在的矩形內即可
* @param{Sprite} sp: Sptite的實例
*/
isCollideWith(sp) {
let spX = sp.x + sp.width / 2
let spY = sp.y + sp.height / 2
if ( !this.visible || !sp.visible )
return false
return !!( spX >= this.x
&& spX <= this.x + this.width
&& spY >= this.y
&& spY <= this.y + this.height )
}
}
Spirite類
constructor
構造器
- 根據輸入圖片路徑,高度,寬度,初始坐標(x,y)生成一個精靈
- 這里的x,y 是指圖片的左上角坐標
- 應注意的一點是此處所有參數均有缺省值
- 初始visible設置為true,即可見
drawToCanvas(ctx)
將精靈畫於畫布上
- 如果不可見,那么不畫到畫布上
- 如果可見,根據該精靈的x,y坐標
isCollideWith(sp)
- 根據傳入物體的左上角的坐標和大小計算中心坐標
- 若兩個物體中任意一個不可見,則無需計算,直接返回失敗
- 判斷傳入物體的中心坐標有沒有在該物體的方框之內
animation.js
動畫類所在文件
代碼
import Sprite from './sprite'
import DataBus from '../databus'
let databus = new DataBus()
const __ = {
timer: Symbol('timer'),
}
/**
* 簡易的幀動畫類實現
*/
export default class Animation extends Sprite {
constructor(imgSrc, width, height) {
super(imgSrc, width, height)
// 當前動畫是否播放中
this.isPlaying = false
// 動畫是否需要循環播放
this.loop = false
// 每一幀的時間間隔
this.interval = 1000 / 60
// 幀定時器
this[__.timer] = null
// 當前播放的幀
this.index = -1
// 總幀數
this.count = 0
// 幀圖片集合
this.imgList = []
/**
* 推入到全局動畫池里面
* 便於全局繪圖的時候遍歷和繪制當前動畫幀
*/
databus.animations.push(this)
}
/**
* 初始化幀動畫的所有幀
* 為了簡單,只支持一個幀動畫
*/
initFrames(imgList) {
imgList.forEach((imgSrc) => {
let img = new Image()
img.src = imgSrc
this.imgList.push(img)
})
this.count = imgList.length
}
// 將播放中的幀繪制到canvas上
aniRender(ctx) {
ctx.drawImage(
this.imgList[this.index],
this.x,
this.y,
this.width * 1.2,
this.height * 1.2
)
}
// 播放預定的幀動畫
playAnimation(index = 0, loop = false) {
// 動畫播放的時候精靈圖不再展示,播放幀動畫的具體幀
this.visible = false
this.isPlaying = true
this.loop = loop
this.index = index
if ( this.interval > 0 && this.count ) {
this[__.timer] = setInterval(
this.frameLoop.bind(this),
this.interval
)
}
}
// 停止幀動畫播放
stop() {
this.isPlaying = false
if ( this[__.timer] )
clearInterval(this[__.timer])
}
// 幀遍歷
frameLoop() {
this.index++
if ( this.index > this.count - 1 ) {
if ( this.loop ) {
this.index = 0
}
else {
this.index--
this.stop()
}
}
}
}
准備內容
- 引入Spirit類和DataBus類
- 生成一個databus對象
- 確定一個Symbol對象
Animation類
繼承於Sprite類
constructor
構造器
- 先用圖片路徑和寬度高度初始化超類(spirit類)
- 一些基本的動畫設置參數,如備注所述作用
initFrames(imgList)
初始化幀動畫的所有幀
- 對傳入參數imgList進行遍歷
- forEach是對js中對數組遍歷的一種方式
- =>是匿名函數的語法
aniRender(ctx)
把播放中的幀畫到畫布上
- 通過調用drawImage畫上動畫在該時刻應該有的圖像
- 該函數在main.js中的render中有調用
playAnimation(index = 0, loop = false)
- 將精靈圖的可見設為false
- 在本例子中有一個敵機被擊毀,發生了敵機爆炸,展示爆炸的動畫
- 設置正在播放
- 將是否循環的情況設置為初始設置的(初始設置為不循環)
- 判斷是否有動畫切換間隔和幀數
- 有的話設置定時器,使用函數setInterval
- setInterval函數
- 第一個參數是回調函數,是在這個過程中不斷調用的函數
- 第二個參數是間隔
- 整個函數的含義就是在該間隔內不斷調用傳入的回調函數
- (博主猜測一般情況來說主函數中的圖像切換頻率大於該間隔,這樣才能體現動畫的變化)
stop()
停止幀動畫播放
- 將播放設置為false
- 清除原本設置的定時器
frameLoop()
幀遍歷
- 幀計數變量index加加
- 若幀數大於圖片數-1(由於計數從0開始)
- 如果要求循環,將index置0
- 否則將index--,即設置為最后一張圖片,並且調用stop()函數暫停