簡介
通常情況下,在玩 2D 游戲或渲染 HTML5 畫布時,需要執行優化,以便使用多個層來構建一個合成的場景。在 OpenGL 或 WebGL 等低級別渲染中,通過逐幀地清理和繪制場景來執行渲染。實現渲染之後,需要優化游戲,以減少渲染的量,所需成本因情況而異。因為畫布是一個 DOM 元素,它使您能夠對多個畫布進行分層,以此作為一種優化方法。
常用的縮寫
CSS: Cascading Style Sheets(級聯樣式表)
DOM: Document Object Model(文檔對象模型)
HTML: HyperText Markup Language(超文本標記語言)
本文將探討對畫布進行分層的合理性。了解 DOM 設置,從而實現分層的畫布。使用分層進行優化需要各種實踐。本文還將探討一些優化策略的概念和技術,它們擴展了分層方法。
您可以下載在本文中使用的示例的源代碼。
選擇優化策略
選擇最佳優化策略可能很難。在選擇分層的場景時,需要考慮場景是如何組成的。大屏幕上固定物的渲染經常需要重用若干個組件,它們是進行研究的極佳候選人。視差或動畫實體等效果往往需要大量的變化的屏幕空間。在探索您的最佳優化策略時,最好注意這些情況。雖然畫布的分層優化需要采用幾種不同的技術,但在正確應用這些技術後,往往會大幅提升性能。
設置層
在使用分層的方法時,第一步是在 DOM 上設置畫布。通常情況下,這很簡單,只需定義畫布元素,將其放入 DOM 中即可,但畫布層可能需要一些額外的樣式。在使用 CSS 時,成功地實現畫布分層有兩個要求:
各畫布元素必須共存於視區 (viewport) 的同一位置上。
每個畫布在另一個畫布下面必須是可見的。
圖 1顯示了層設置背後的通用重疊概念。
圖 1. 層示例
設置層的步驟如下:
將畫布元素添加到 DOM。
添加畫布元素定位樣式,以便支持分層。
樣式化畫布元素,以便生成一個透明的背景。
設置畫布重疊堆棧
在 CSS 中創建一個重疊堆棧 (overlay stack) 可能需要少量的樣式。使用 HTML 和 CSS 有許多方法進行重疊。本文中的示例使用一個
標簽來包含畫布。 標簽指定了一個惟一 ID,它將樣式應用於其子 HTML5 畫布元素,如清單 1所示。
清單 1. 畫布定位樣式
CSS Code復制內容到剪貼板
#viewport {
/**
* Position relative so that canvas elements
* inside of it will be relative to the parent
*/
position: relative;
}
#viewport canvas {
/**
* Position absolute provides canvases to be able
* to be layered on top of each other
* Be sure to remember a z-index!
*/
position: absolute;
}
容器
通過將所有子畫布元素樣式化為使用絕對定位來完成重疊要求。通過選擇讓#viewport使用相對定位,您可以適應未來的發展,因此,應用於子樣式的絕對布局樣式將會是相對於#viewport容器的樣式。
這些 HTML5 畫布元素的順序也很重要。可以按元素出現在 DOM 上的順序進行順序管理,也可以按照畫布應該顯示的順序來樣式化 z-index 樣式,從而管理順序。雖然並非總是如此,但其他樣式可能也會影響渲染;在引入額外的樣式(比如任何一種 CSS 轉換)時要小心。
透明的背景
通過使用重疊可見性來實現層技術的第二個樣式要求。該示例使用這個選項來設置 DOM 元素背景顏色,如清單 2所示。
清單 2. 設置透明背景的樣式表規則
XML/HTML Code復制內容到剪貼板
canvas {
/**
* Set transparent to let any other canvases render through
*/
background-color: transparent;
}
將畫布樣式化為擁有一個透明背景,這可以實現第二個要求,即擁有可見的重疊畫布。現在,您已經構造了標記和樣式來滿足分層的需要,所以您可以設置一個分層的場景。
分層方面的考慮因素
在選擇優化策略時,應該注意使用該策略時的所有權衡。對 HTML5 畫布場景進行分層是一個側重於運行時內存的策略,用於獲得運行時速度方面的優勢。您可以在頁面的浏覽器中增加更多的權重,以獲得更快的幀速率。一般來說,畫布被視為是浏覽器上的一個圖形平面,其中包括一個圖形 API。
通過在 Google Chrome 19 進行測試,並記錄浏覽器的選項卡內存使用情況,您可以看到內存使用的明顯趨勢。該測試使用了已經樣式化的
(正如上一節中討論的那樣),並生成了放置在 上的用單一顏色填充的畫布元素。畫布的大小被設定為 1600 x 900 像素,並從 Chrome1 的任務管理器實用程序收集數據。表 1顯示了一個示例。
在 Google Chrome 的 Task Manager 中,您可以看到某個頁面所使用的內存量(也稱為 RAM)。Chrome 也提供 GPU 內存,或者是 GPU 正在使用的內存。這是常見信息,如幾何形狀、紋理或計算機將您的畫布數據推送到屏幕可能需要的任何形式的緩存數據。內存越低,放在計算機上的權重就會越少。雖然目前還沒有任何確切的數字作為依據,但應始終對此進行測試,確保您的程序不會超出極限,並使用了過多的內存。如果使用了過多的內存,浏覽器或頁面就會因為缺乏內存資源而崩潰。GPU 處理是一個遠大的編程追求,已超出本文的討論范圍。您可以從學習 OpenGL 或查閱 Chrome 的文檔(請參閱參考資料)開始。
表 1. 畫布層的內存開銷
在表 1中,隨著在頁面上引入和使用了更多的 HTML5 畫布元素,使用的內存也越多。一般的內存也存在線性相關,但每增加一層,內存的增長就會明顯減少。雖然這個測試並沒有詳細說明這些層對性能帶來的影響,但它確實表明,畫布會嚴重影響 GPU 內存。一定要記得在您的目標平台上執行壓力測試,以確保平台的限制不會導致您的應用程序無法執行。
當選擇更改某個分層解決方案的單一畫布渲染周期時,需考慮有關內存開銷的性能增益。盡管存在內存成本,但這項技術可以通過減小每一幀上修改的像素數量來完成其工作。
下一節將說明如何使用分層來組織一個場景。
對場景進行分層:游戲
在本節中,我們將通過重構一個滾動平台跑步風格的游戲上的視差效果的單畫布實現,了解一個多層解決方案。圖 2顯示了游戲視圖的組成,其中包括雲、小山、地面、背景和一些交互實體。
圖 2. 合成游戲視圖
在游戲中,雲、小山、地面和背景都以不同的速度移動。本質上,背景中較遠的元素移動得比在前面的元素慢,因此形成了視差效果。為了讓情況變得更為復雜,背景的移動速度會足夠慢,它每半秒鐘才重新渲染一次。
通常情況下,好的解決方案會將所有幀都清除並重新渲染屏幕,因為背景是一個圖像並且在不斷變化。在本例中,由於背景每秒只需變化兩次,所以您不需要重新渲染每一幀。
目前,您已經定義了工作區,所以可以決定場景的哪些部分應該在同一個層上。組織好各個層之後,我們將探討用於分層的各種渲染策略。首先,需要考慮如何使用單個畫布來實現該解決方案,如清單 3所示。
清單 3. 單畫布渲染循環的偽代碼
XML/HTML Code復制內容到剪貼板
/**
* Render call
*
* @param {CanvasRenderingContext2D} context Canvas context
*/
function renderLoop(context)
{
context.clearRect(0, 0, width, height);
background.render(context);
ground.render(context);
hills.render(context);
cloud.render(context);
player.render(context);
}
像清單 3中的代碼一樣,該解決方案會有一個render函數,每個游戲循環調用或每個更新間隔都會調用它。在本例中,渲染是從主循環調用和更新每個元素的位置的更新調用中抽象出來。
遵循 “清除到渲染” 解決方案,render會調用清除上下文,並通過調用屏幕上的實體各自的render函數來跟蹤它。清單 3遵循一個程序化的路徑,將元素放置到畫布上。雖然該解決方案對於渲染屏幕上的實體是有效的,但它既沒有描述所使用的所有渲染方法,也不支持任何形式的渲染優化。
為了更好地詳細說明實體的渲染方法,需要使用兩種類型的實體對象。清單 4顯示了您將使用和細化的兩個實體。
清單 4. 可渲染的Entity偽代碼
XML/HTML Code復制內容到剪貼板
var Entity = function() {
/**
Initialization and other methods
**/