微信小遊戲 demo 飛機大戰 代碼分析 (三)(spirit.js, animation.js)

微信小遊戲 demo 飛機大戰 代碼分析(三)(spirit.js, animation.js)

微信小遊戲 demo 飛機大戰 代碼分析(一)(main.js)html

微信小遊戲 demo 飛機大戰 代碼分析(二)(databus.js)canvas

微信小遊戲 demo 飛機大戰 代碼分析(四)(enemy.js, bullet.js, index.js)數組

本博客將使用逐行代碼分析的方式講解該demo,本文適用於對其餘高級語言熟悉,對js還未深刻了解的同窗,博主會盡量將全部遇到的不明白的部分標註清楚,如有不正確或不清楚的地方,歡迎在評論中指正微信

本文的代碼均由微信小遊戲自動生成的demo飛機大戰中獲取ide

spirit.js

遊戲基礎的精靈類函數

代碼oop

/**
 * 遊戲基礎的精靈類
 */
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)

將精靈畫於畫布上this

  • 若是不可見,那麼不畫到畫布上
  • 若是可見,根據該精靈的x,y座標

isCollideWith(sp)

  • 根據傳入物體的左上角的座標和大小計算中心座標
  • 若兩個物體中任意一個不可見,則無需計算,直接返回失敗
  • 判斷傳入物體的中心座標有沒有在該物體的方框以內

animation.js

動畫類所在文件code

代碼

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()函數暫停
相關文章
相關標籤/搜索