原生canvas遊戲性能優化

隨着微信小遊戲的推出,其全面支持以往的H5遊戲開發,微信借小遊戲的社交方式完全激活小程序。一樣的,也算是從新吹起了H5遊戲的風口。前端

能夠預見的是,藉助小遊戲的風,前端遊戲開發這一分支也會燃起來了。在遊戲開發中,最使人難受的也許就是性能優化了吧。本人在整理過往幾回遊戲開發的經歷中,總結了一些常被忽視的優化小措施,與諸君分享。canvas


針對遊戲性能優化,首先,咱們要知道咱們優化的目標是什麼?每每咱們以爲性能優化很難,是由於咱們不肯定優化目標是什麼,針對什麼進行優化。小程序

在我看來,性能優化的實質,實際上就是儘量的減小等待時間和內存使用。api

有了目標有好辦了,接下來,咱們須要知道咱們經過優化哪些目標能夠減小代碼執行和內存使用。我粗略的分爲了3個方面:瀏覽器

  • Canvas,原生canvas遊戲,首要目標天然就是canvas
  • 內存使用,果7以前safari運行內存只有100M,濫用內存直接給你強制鎖死
  • 多線程,兩條腿走路確定比一條腿快多了啊

Canvas

離屏canvas

場景:針對須要大量使用且繪圖繁複的靜態場景性能優化

實現:對象內放置一個私有canvas,初始化時將靜態場景繪製完備,須要時直接拷貝內置canvas的圖像便可微信

//每一幀重繪時
setInterval(function () {

    context.fillRect(x,y,width,height)
    context.arc(x,y,r,sA,eA)
    context.strokeText('hehe', x, y)
    
}, 1000/60)
複製代碼

設置離屏canvas網絡

let background = {
    width: 400,
    height: 400,
    canvas: document.createElement('canvas'),
    init: () => {
        let self = this
        let ctx = self.canvas.getContext('2d')
        
        self.canvas.width = self.width
        self.canvas.height = self.height

        ctx.fillRect(x,y,width,height)
        ctx.arc(x,y,r,sA,eA)
        ctx.strokeText('hehe', x, y)

    }
    
}

background.init()

setInterval(() => {

    context.drawImage(background.canvas, background.width, background.height, 0, 0);
    
}, 1000/60)
複製代碼

不設置離屏canvas的狀況下,每幀繪製會調用3次繪圖api;設置離屏canvas後,每幀只用調用一次api。多線程

實質:減小調用api的次數,減小代碼執行語句,從而減小每幀渲染時間,從而提升動畫流程度。異步

狀態修改

場景:針對須要頻繁修改canvas對象的渲染狀態 (fillStyle, strokeStyle ...)

實現:按canvas狀態分別繪製,而不是按對象進行繪製

混合繪製

for (let i = 0; i < line.length; i++) {

    let e = line[i]

    context.fillStyle = i % 2 ? '#000': '#fff'

    context.fillRect(e.x, e.y, e.width, e.height)

}
複製代碼

不一樣狀態分別繪製:

context.fillStyle = '#000'

for (let i = 0; i < line.length / 2 - 1; i++) {

    let e = line[i * 2 + 1]

    context.fillRect(e.x, e.y, e.width, e.height)

}

context.fillStyle = '#fff'

for (let i = 0; i < line.length / 2 - 1; i++) {

    let e = line[i * 2]
    
    context.fillRect(e.x, e.y, e.width, e.height)

}
複製代碼

先後比較看,雖然循環次數沒變,但循環內調用的語句變少了,即不在循環內修改canvas狀態了。

實質:減小canvas api的調用,不用在每次根據對象屬性去修改canvas的狀態,而是將具備相同狀態的對象提出,批量渲染。

分層和局部重繪

場景:針對場景中大背景變化緩慢,而角色的狀態變換頻繁

實現:將場景按狀態變換快慢進行層次劃分,設置不一樣的透明度和z-index進行層級疊加。

實質:經過分層,對連續幀中的相同場景不重複渲染,減小渲染所需的canvas api的調用。

但在微信小遊戲中,本方法不能使用,由於微信小遊戲中有全局惟一canvas,其餘canvas都是離屏canvas,不能顯示。

requestAnimationFrame

這個不存在什麼場景,就是一把梭,無腦直接上RAF,別再setInterval了。

簡單點說,RAF是瀏覽器根據頁面渲染的狀況,自行選擇下一幀繪製的時機。

可是有一個tip須要注意,RAF無論理回調函數,即在RAF回調被執行前,若是RAF屢次調用,其回調函數也會屢次調用。因此須要作好防抖節流。否則會致使RAF的回調函數在同一幀中重複調用,形成沒必要要的計算和渲染的消耗。

const animation = timestamp => console.log('animation called at', timestamp)

window.requestAnimationFrame(animation)
window.requestAnimationFrame(animation)
複製代碼

內存優化

對象池

場景:針對遊戲中須要頻繁更新和刪除 的角色

實現:對象池維護一個裝着空閒對象的池子,若是須要對象的時候,不是直接new,而是從對象池中取出,若是對象池中沒有空閒對象,則新建一個空閒對象。

const __ = {
  poolDic: Symbol('poolDic')
}

/**
 * 簡易的對象池實現
 * 用於對象的存貯和重複使用
 * 能夠有效減小對象建立開銷和避免頻繁的垃圾回收
 * 提升遊戲性能
 */
export default class Pool {
  constructor() {
    this[__.poolDic] = {}
  }

  /**
   * 根據對象標識符
   * 獲取對應的對象池
   */
  getPoolBySign(name) {
    return this[__.poolDic][name] || ( this[__.poolDic][name] = [] )
  }

  /**
   * 根據傳入的對象標識符,查詢對象池
   * 對象池爲空建立新的類,不然從對象池中取
   */
  getItemByClass(name, className) {
    let pool = this.getPoolBySign(name)

    let result = (  pool.length
                  ? pool.shift()
                  : new Object()  )

    return result
  }

  /**
   * 將對象回收到對象池
   * 方便後續繼續使用
   */
  recover(name, instance) {
    this.getPoolBySign(name).push(instance)
  }
}
複製代碼

實質:減小內存的使用。每次建立一個對象,都須要分配一點內存,而因爲瀏覽器的回收機制,致使會有大量無用的對象的累加,白白消耗大量的內存。


多線程

Worker

場景:針對須要進行大量計算任務

實現:使用worker單獨開啓線程進行並行計算,主線程仍執行本身的任務。

實質就是並行計算,避免進程堵塞。任務計算須要的時間是不會減小的,形象點來講就是從一條腿走路變成兩條腿走路

//main.js
//建立worker線程
let worker = new Worker('worker.js')
//監聽worker線程的返回事件
worker.onmessage = (e) => {
    //e worker線程的返回對象
}
//發送消息
worker.postMessage(obj)

//worker.js
//監聽主線程的執行請求
onmessage = (e) => {
    //執行對象e
    
    postMessage(result)
}
複製代碼

實質:並行計算,能夠認爲計算任務與主線程工做是異步的,互不干擾。由於是將計算任務所有交給worker,全部計算時間是不會減小的。

對象池不只能夠針對對象,還能夠針對worker進行線程池的管理,有興趣的朋友能夠試試。


其實除了上述3個方面,還有一個很是重要的優化目標,那就是網絡優化,但這也是咱們常說的瀏覽器性能優化的終點內容,因此關於網絡優化,各位就請移步其餘大神的文章,我也就再也不賣弄我那一點三腳貓技術了。各位朋友有什麼其餘的優化措施的,歡迎交流。

相關文章
相關標籤/搜索