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


微信小游戲 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()函數暫停


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM