完全分享,共同進步——我開發的第一款HTML5遊戲《驢子跳》

原文鏈接:http://www.iteye.com/topic/1122395

在線演示:http://yujianshenbing.w108.mc-test.com/game/donkeyjump/index.html(如果無法訪問,請大家暫時先直接下載源碼運行)

源碼下載:請查看附件

操作方法:遊戲開始後,按鍵盤“A”或“D”控制左右方向,也可以通過方向鍵控制。

  (請了解:遊戲中所有圖片和音樂均來自手機版同名遊戲,本遊戲僅供學習參考。本文所有內容及結論均屬個人意見,如有不同見解,歡迎指正。)

 

  經過兩個多月斷斷續續的開發,我的第一款遊戲《驢子跳》終於完成了,此時,我已經迫不及待地想跟大家分享這個過程,包括學習方法、遊戲源碼、和經驗心得;本文的目的,是希望能幫助更多的人快速地加入到HTML5的大團隊,同時也希望能得到各種大俠的指點。

 

  本文所有內容基於個人的學習過程和經驗,因此在整理文章的過程中,我也假設你目前對遊戲開發一無所知,甚至對HTML和JavaScript也不太瞭解。

  我將文章分爲5個部分:

 

  1. 我將會告訴你如何學習遊戲開發,如何入門HTML5
  2. 以我的遊戲框架爲例,初步瞭解遊戲模型和組成
  3. 通過講解我的“遊戲邏輯結構”,讓你真正可以開始動手創建HTML5遊戲
  4. 分享諸多親身體驗的性能問題和解決方案
  5. 一些其它的東西

 

 

一、如何學習HTML5遊戲開發?

  如果是剛接觸HTML5,也是第一次開發遊戲,過程中難免會遇到這樣那樣的問題,我所遇到的第一個問題就是:我對HTML5還不是很瞭解。因此我首先要分享的就是學習方法和路線:

  如果你還不是很熟悉HTML、CSS、和JavaScript,請不要急於求成,只要打好基礎,一切都會變得容易起來。

  如何纔算是“熟悉”?

 

  1. HTML常用標籤你全都認識並能說出它們相同、和不同的特性嗎?
  2. CSS中所有屬性是否熟悉?特別是CSS3中的新特性和動畫?
  3. 是否已經完全明白JavaScript面向對象、閉包、原型鏈、作用域等概念,並能熟練運用?
  4. 是否掌握了一系列JavaScript性能優化的方法和經驗?

 

 

  如果你在回答這些問題時還不是很有信心,我建議先補充下這方面的知識。

  也許你會問:做HTML5遊戲不是主要使用Canvas畫布和JavaScript嗎?爲什麼對HTML和CSS還有這麼多要求?最開始,我也是這樣以爲的,不過後來我發現HTML5遊戲中並非只有Canvas,其實CSS仍然顯得非常重要,Canvas應該只做一些它更擅長的事,比如動畫和特效,而其它的事情交給其它方式來實現吧。

  (題外話:這就像前幾年流行web2.0時,不少公司和客戶,都明確規定頁面不允許出現table標籤,於是乎我們寫了一堆UL標籤和CSS代碼,來實現類似於表格的效果,並擔心各瀏覽器的差異。我至今仍在懷疑,這種做法除了符合所謂的“標準”,還有什麼明顯的優勢?)

  言歸正傳,例如我們要開發一個遊戲“熱血傳奇”,首先分析下圖:圖中藍色背景的部分應該使用Canvas來繪製,而紅色背景的部分則應該使用普通的HTML標籤和CSS來開發。

 

  判定標準主要是依據該模塊的效果複雜度、和刷新頻率,在“熱血傳奇”中,界面包含人物、地圖、技能效果等主場景(藍色背景部分);還有狀態工具欄、人物屬性窗口、物品窗口、技能窗口等(紅色背景部分)。

  很明顯,主場景中的人物、技能效果、地圖等隨時都在發生變化,這種場景才能體現出Canvas的優勢;而其它的例如狀態工具欄、人物屬性窗口等,更新的頻率很低,只有在用戶驅動下才會發生改變,且不包含複雜的效果,因此我們可以把它當成是一個普通頁面的一部分,使用普通的HTML結構和CSS樣式,再加上一些控制邏輯,即可實現。

  爲什麼一定要儘量使用HTML結構和CSS的方式來實現呢?因爲這會降低開發的複雜度、工作量和成本,更重要的是,在性能上會有更好的體現(關於性能上的問題,後面會詳細討論)。

 

  假設你對這些知識都已經非常瞭解了,下一步要做的,就是全面瞭解Canvas畫布。

  剛開始瞭解它時,你會發現它並非你想象中的那麼完美,它只提供了一系列“簡陋”的方法,你甚至會懷疑使用它們真的能開發出效果絢麗的遊戲嗎?不過仔細想想,只要在這套API上稍作擴展,還有什麼事情是我們辦不到的呢?

  關於Canvas的API具體內容,這裏不作討論,還不清楚的同學,可以參考官方文檔,或中文版API:http://wenku.baidu.com/view/d841013d0912a2161479292d.html

  在獲取到這些文檔之後,如何入手呢?別想太多,先從頭到尾用幾遍,只要記住它有哪些方法,以及這些方法都是用來做什麼的、怎麼用的就可以了。關於API中不明白的地方,百度和Google會告訴你答案。

 

  如果你已經瞭解了API中的方法,可能會很迷惑:如何才能將這些API熟練地運用到遊戲開發中?那麼請看這裏吧:

  你可以試着瞭解一些遊戲中的基本概念和組織結構,這會讓你開始“忙起來”,如果你看不懂網上那麼多專業的遊戲開發資料,或者是不知道該從何入手,我推薦可以看看“大城小胖”的視頻教程《手把手教你開發HTML5遊戲》,地址是:http://v.youku.com/v_playlist/f5711740o1p0.html,我感覺這是一個很詳細的入門視頻,因爲我也是看它入門的(入門之前,可能我跟你現在一樣,對遊戲開發一竅不通,因此你完全可以有信心比我做的更好)。

  看視頻的時候,你應該先動手創建一個工程,跟着視頻寫同樣的代碼,視頻中沒有講清楚的概念和環節,網上可以查的到;如果看完視頻感覺沒有徹底明白的話,可以再看幾次,直到你感覺視頻中介紹的內容你全都掌握了(這個視頻我看了不下10遍,同時一邊寫、一邊在網上擴展更多的相關知識,這個過程大約花了我兩到三個星期。)

 

  當學習完以上部分內容後,你對遊戲開發的內容有了一些瞭解,也知道自己首先應該做的,就是編寫一個簡單的遊戲引擎。但你卻還不知道一個完整的遊戲引擎在技術上到底包含了哪些部分?應該劃分爲哪些模塊?各個模塊間的耦合關係如何確定?確實,對於一個沒有任何遊戲開發經驗的人來說,這部分內容需要一段時間去琢磨(至少我花了不少的時間在這個階段)。

  這時,我建議你可以多分析目前已有的遊戲框架源碼,仔細閱讀,認真體會;如果你對AS3和Flex比較熟悉,那麼還可以瞭解一些Flex遊戲框架,畢竟這東西和開發語言關係不大。

  下面我推薦一些國產的框架給大家參考:

  casualjs:http://code.google.com/p/casualjs/,flashlizi久久之前就開發出來的一款HTML5遊戲框架,風格、組件和事件管理與AS3非常類似,目前已經停止更新了;且源碼中有一些地方還有問題,比如常用的copy函數;源碼和設計結構很值得學習。

  quarkjs:https://github.com/quark-dev-team/quarkjs,這套框架是我最近才發現,更新頻率很高,跟casualjs同一作者,我理解應該算是casualjs的升級版本了。

  最後推薦的就是我分享的遊戲源碼了,雖有還有許多不完善的地方,但格式清晰,註釋完整,比較適合學習(下載源碼後,myEngine目錄爲遊戲框架源碼)。

  這個過程中最重要的就是堅持,困難肯定是有的,但問題一定也是可以解決的。

  (小插曲:我在學習過程中,經常會向各種大俠請教,雖然他們並不認識我。我有段時間連續幾天通過微博求 助於“大城小胖”,估計那段時間他快被我搞煩了,其實那段時間我自己也快被自己搞瘋了;在這裏感謝小胖~)

 

  好了,看到這裏,你已經掌握了一套學習方法,它會帶着你,完成你的第一個HTML5遊戲。更重要的是,完成之後,請記得一定要分享出來,因爲這樣做不僅僅會幫助更多的人,你也會得到進一步的提高。

 



二、以myEngine爲例開發一套遊戲框架

 

  接下來我們進入第二步,我將介紹這個遊戲的相關目錄和組件,在這之前,請先下載遊戲的源碼。

  當你打開donkeyjump目錄後,能看到這裏存放着遊戲中的所有資源,包括源碼、音頻和圖像文件。我先介紹一下目錄的結構,請看下圖:

 

  (如果你也想把某一款手機遊戲移植到HTML5版本,首先可以先下載一個該遊戲的APK安裝包,一般在安卓市場都可以搜索的到:http://game.hiapk.com/,前提是這款遊戲存在對應的Android版本。下載之後將apk擴展名修改爲rar並解壓,這樣你就可以從解壓後的目錄中找到所有的圖片和音頻資源。注意:有些遊戲對資源進行了壓縮和處理,這樣你就很難獲取到了。)

 

  在遊戲源碼的根目錄,當你打開index.html就可以快速預覽遊戲的效果了(當然,這需要你的瀏覽器支持HTML5部分特性)。

  遊戲的核心框架是myEngine目錄,這也是本節介紹的重要。myEngine目錄中包含了與遊戲業務邏輯無關的核心庫,它與你所接觸過的jQuery、Ext等框架一樣,封裝了一系列常用的處理邏輯;在它的基礎上,你可以更簡單地編寫少量的代碼,來完成你的遊戲。

 

  按照遊戲index.html文件中引入的腳本順序,我將依次說明遊戲框架中每個文件的含義:

 

  • 首先是core目錄下的my.js,這是框架的核心文件,它包含了一些常用的公共函數,如果你已經瞭解了JavaScript面向對象相關的知識,那麼很容易可以看懂文件中所有的代碼。
  • component/Component.js:Component是所有遊戲組件的基類,所有的遊戲組件均繼承自它,Component類提供了組件對象的創建、初始化、銷燬等公共方法。
  • component/DisplayObject.js:DisplayObject是所有可顯示組件的基類,遊戲中所有能顯示的組件,都應該繼承它,因爲它提供了一套統一的處理對象顯示的方法:顯示隱藏控制、透明度、旋轉、翻轉、縮放、渲染控制等。
  • component/DisplayObjectContainer.js:DisplayObjectContainer是一個組件容器類,它本身也是一個DisplayObject對象,但它可以容納其它更多的DisplayObject和DisplayObjectContainer對象,便於統一管理。例如:一個房間內的場景包含了桌子、椅子、衣櫃、書架等DisplayObject(或DisplayObjectContainer)對象,而這個房間就是一個DisplayObjectContainer對象,房間被銷燬,房間內的所有東西也不會再存在;這樣你就不需要再把房間內的東西一個個地銷燬掉。
  • component/Bitmap.js:圖像對象,如果你要在遊戲中顯示一副圖像,可以創建一個Bitmap對象。
  • component/Animation.js:幀動畫控制類,由多幅圖像組成,在固定的頻率中不斷切換,形成動畫的效果。使用它,可以方便地創建一個幀動畫,並控制它的顯示、隱藏和播放等行爲。
  • component/Sprite.js:遊戲中所有精靈的基類,如果對“精靈”的概念還不是很清楚,建議按照第一節中介紹的學習過程再鞏固一下。它提供了一個精靈的基礎方法和屬性,包括移動、動畫控制和碰撞檢測。
  • component/Viewport.js:視口對象,遊戲地圖可能會很大,但能夠同時看得到的區域可能只是一個固定的尺寸,當遊戲角色或場景移動時,地圖也會移動,玩家會感覺是通過一個視口在觀察角色和地圖的移動。這裏的視口對象,就是用來處理視口的移動,以及保存視口狀態的。
  • component/Layer.js:遊戲的分層,一個遊戲中可能包含許多內容,比如主角、玩家、NPC、怪獸、道具、效果、地圖和其它場景,我們沒必要把它們堆到一起,這樣你無法進行管理和維護,因此我將它們放到多個層級,就可以方便地對每個層級內的元素進行統一控制。
    這裏的分層不僅僅是在邏輯上將不同類型的元素分離開,不同的Layer對象還可以配置到不同的Canvas畫布上,這是提升性能的一種重要的方式,後面會詳細討論。
  • component/Game.js:遊戲基類,用於控制遊戲的幀頻、畫布的刷新、開始和結束遊戲,以及遊戲時間相關的記錄。
  • event/KeyEvent.js:監聽用戶按鍵事件,如果你查看過這個文件的源碼,相信我不用作太多介紹你就會明白。它不會綁定具體的事件在用戶的某個按鍵上,它只提供一些控制和查詢當前按鍵狀態的方法。
  • utils/ImageManager.js:圖像管理類,使用Canvas繪圖前,首先需要保證圖片已經被完全加載完畢,而遊戲中使用的圖片非常多,因此如何統一控制加載和管理,就由ImageManager來發揮了。
  • utils/DOM.js:提供了一套類似於jQuery的DOM操作方法,之所以不使用jQuery,是因爲對這個遊戲來說,jQuery顯得有些大材小用,沒有必要爲了使用其中的幾個方法,加載整個庫。
  • utils/Math.js:提供一些基礎的算數運算方法。
  • utils/buzz.js:這是一個第三方音頻管理庫,作用和ImageManager類似,不過是用於控制音頻文件的加載和播放。其實框架中是包含了一個我自己寫的音頻管理類,這裏之所以使用第三方庫,是因爲我還不能完全確保那個音頻管理類的穩定性。
  • 另外,框架中還包含了一些遊戲中沒有使用到的文件,比如:ScriptManager(腳本動態加載和管理)和Astar(自動尋路算法),這裏我就不作介紹了。

 

 

  以上是遊戲框架中的全部內容,具體細節建議閱讀源碼,源碼中標註有詳細的註釋。

  如果你接觸過Ext框架或Flex,你會很容易理解這裏大部分類的作用和功能,因爲它們有許多相似之處。

 

  這套框架我一共重寫了4次,每一次都會有許多提升。這裏所說的提升並非是指功能的增強,相反,這個版本和第1次完成時相比,功能已經被刪減掉了一大部分,之所以這樣做,僅僅是爲了做到需求、工作量和性能之間的平衡(性能相關的問題,我們放到後面再討論),我認爲在能實現需求的前提下,最重要的就是儘可能的簡單、快速。

 

  我在這裏回顧一下之前刪減掉的一些功能:

 

  • 類似DOM Level 2中的事件模型,包含EventEventDispatcherEventManager和Component下具體子類的事件模型類。之所以刪掉它們,是因爲我完全可以使用類似DOM Level 1中的事件模型來代替它們,而這樣做會更簡單,更高效。當然也會犧牲一些擴展性和耦合度,但我認爲這是值得的。
  • 以前的Animation類,是一個繼承自DisplayObjectContainer的子類,它包含了一系列Frame(單幀)對象,而Frame對象中又包含了Bitmap(圖像)對象和CollRect(碰撞區域)對象;當初之所以有這麼多東西,是因爲考慮到動畫對圖像文件和繪製圖形之間的轉換,以及支持多種碰撞檢測方式。這種做法後來也被我否決了,原因與和前面一點一樣,出於簡單開發和高效性能的考慮。
    而現在優化後的Animation類十分簡單,它本身只是一個Component組件,通過簡單配置圖像URL和幀數據,就可以實現一個幀動畫。當然,這種做法就受到許多約束,例如:一個動畫中所有的幀圖像必須是同一個Image對象,還有Animation本身不提供渲染的方法,它必須依附在一個Sprite(精靈)對象中。
  • 刪除了LevelScene類,Level類是用來做遊戲關卡控制和調度關卡數據的,而Scene類用作場景的切換、初始化、緩存場景數據以及處理Layer(分層)的視差效果。

 

 

  現在我把分層的視差效果直接放到了Layer本身,通過定義Layer對象的distance屬性(即定義分層與視口之間的距離),就會自動形成視差效果。

  例如:在這個遊戲中,驢子剛開始跳躍時,驢子身後的房子、山丘、高山、和天空在視覺上的移動速度是不一樣的,物體距離相對驢子越遠,視覺上移動距離越小;這些背景實際上是存儲在多個Layer中,而我只需要通過定義每個Layer的distance屬性,就可以實現這個效果。

 

  如果你想開發一套成熟的遊戲框架,這些功能或許是非常必要的。因此,我並不能保證刪減這些功能是正確的做法,但對於具體需求來說,我認爲這樣做會更合適。

  (如果你也需要這些代碼,可以聯繫我。)

 



三、開發一個“驢子跳”遊戲

 

  到目前爲止,我已經介紹了學習方法、和基礎框架的搭建;此時你應該花更多的時間去編寫和優化你的框架,讓它更簡單、更穩定以及更加“堅固”。如果你還有不明白的地方,可能是我描述的不夠清楚和準確,也可能是你所花的時間還不夠多;無論如何,你都可以聯繫我一起研究學習。

 

  如果你對前面講解的內容沒有太多的困惑,本節將着重討論如何在框架庫的基礎上,開始開發遊戲。這個過程會讓你非常心動,因爲不久你就可以看到遊戲的樣子了。

 

  我第一步要做的就是將整理遊戲資源,提取遊戲中圖片和音頻文件的方法可以參考第二點中的內容。

 

  首先是整理圖片資源,我使用Photoshop對圖片尺寸進行調整,更重要的是將圖片進行合併;這將減少資源的請求數,提高加載效率;另外,這也是我框架庫中Animation類所強制要求的(前面提到過,Animation要求動畫中所有的幀圖像爲同一個Image對象)

  其次是整理音頻文件,目前各瀏覽器還沒有統一支持的音頻格式,因此我們必須使用兩套同樣的音頻文件,關於目前各瀏覽器所支持的音頻格式如下:

 

瀏覽器   支持的音頻格式
IE9 mp3, aac wav
Firefox ogg, wav
Chrome ogg, mp3, aac, wav
Safari mp3, aac, wav
Opera ogg, wav

  遊戲中的音頻文件是mp3格式,爲了兼容Firefox和Opera,我同時還選擇了wav格式。wav格式的文件非常大,爲了減小文件大小,我將音頻進行了壓縮,減小比特率,但音質受到了明顯的影響。可能你會疑惑,爲什麼我不選擇ogg格式呢?這是因爲我購買的虛擬主機竟然不支持訪問ogg格式的文件,Why?

  這裏,我建議你可以仍然使用ogg格式,在保持和mp3同樣音質的情況下,文件大小會比wav小很多。

 

  資源的整理可能需要花上好幾天的時間,不過幸運的是,這些資源我已經整理過一遍,你可以copy過去直接使用。

 

  資源整理完畢後,我創建一個了項目,分好目錄結構,並將資源放在對應的目錄下。然後創建遊戲頁面index.html。

  經過分析,我將盡量能使用HTML和CSS來做的內容獨立開來,使用HTML和CSS進行開發(index.html文件中包含了所有的HTML,css目錄下包含了所有的樣式);還缺少什麼呢?對了,還缺少對DOM的控制邏輯,這麼多的HTML,在遊戲進行到什麼時候顯示?什麼時候隱藏?如何控制?這些具體的邏輯我們都用不管,先編寫一個統一的UI控制類,代碼存放在js/classes/UI.js,UI類不包含任何遊戲邏輯,它只負責處理DOM相關的UI展現和控制,在遊戲中需要使用到它的時候,調用相應的方法即可。

  另外在UI類中,也包含了類似於DOM Level 1的事件管理方式,這是爲了降低DOM對象與遊戲邏輯間的耦合度。

 

  當這部分內容完成的時候,遊戲就有了一個可以看到的雛形,但我們還沒有真正開始呢,因爲我們編寫的遊戲框架庫還沒有派上用場。

 

  我先在js目錄下創建一個main.js,用作遊戲主邏輯的入口文件(在上一節中,我們介紹過,js目錄只用於存放遊戲相關的業務邏輯)。

在main.js中,我先加載了遊戲資源,在資源加載完畢後,創建遊戲對象,並開始監聽和處理DOM相關的事件;當你點擊“開始”按鈕後,遊戲就正式開始了。

 

  遊戲開始後,具體是怎麼運行的,這裏就不做描述,源碼中有詳細的註釋,但我覺得還是有必要簡單描述下js目錄下的所有文件的結構,便於大家查找。

 

  • js/main.js:這個文件剛纔介紹過,是遊戲的主邏輯入口文件。
  • js/classes:遊戲中所有的邏輯代碼都存放在這裏。
  • js/classes/Audio.js:Audio類提供一些基礎方法,用於控制遊戲中音效的播放。
  • js/classes/Cloud.js:當驢子踩到雲朵上時,會產生踐踏效果,Cloud就是踐踏效果類,用於產生踐踏效果對象。
  • js/classes/Donkey.js:驢子類,用於創建驢子實例,提供控制驢子狀態相關的方法。Donkey類繼承自Sprite(精靈)類。
  • js/classes/DonkeyJump.js:DonkeyJump(驢子跳),該類繼承自Game類,是整個遊戲的核心類,主導遊戲整體邏輯和狀態,用於創建遊戲中的分層,負責遊戲初始化、開始、暫停等操作。
  • js/classes/Prop.js:遊戲道具類,遊戲中一共有7種道具,但它們都是Prop類的生成對象。
  • js/classes/Stair.js:雲朵類,遊戲中的雲朵也有7種,包括5種普通雲、脆弱的雲和會移動的雲。雲朵的類型是隨機的,在雲朵被創建時,有一定的機率會同時出現一個道具(道具的類型也是隨機選擇的)。
  • js/classes/UI.js:UI類用於控制DOM的展現邏輯,上文中已經進行過討論。
  • js/frames:遊戲中所有精靈的幀動畫配置數據都存放在這裏;每個文件存放不同精靈的幀動畫數據,此處不一一介紹,只以donke.js爲例,當你打開這個文件,會發現裏面存儲的內容幾乎完全一樣,它定義了每個動畫需要使用的所有幀數據:
    Js代碼  收藏代碼
    1. // 這是某一幀的配置數據  
    2.     x : 0, // 當前幀在動畫的Image對象出現的x軸位置  
    3.     y : 0, // 當前幀在動畫的Image對象出現的y軸位置  
    4.     duration : 10, // 當前幀播放的時間(ms)  
    5.     collRect : [[50, 93, 28, 15]] // 當前幀的矩形碰撞區域  
    6. }  

  單幀配置中的duration表示該幀在動畫過程中所播放的時間,而collRect表示動畫被播放到該幀時,精靈對象的碰撞區域。

  目前我只支持了矩形碰撞,通過分割定義多個矩形碰撞區域,可以提高碰撞監測的準確度。如果需要更加精確的碰撞檢測,就需要使用其它的方式實現,例如像素檢測。

 

  • js/resources:定義遊戲所需要的圖片和音頻資源路徑和配置。
  • js/resources/audios.js:對遊戲中所有的音頻資源進行定義
  • js/resources/images.js:對遊戲中所有圖片資源進行定義

 

 

  當你瞭解了這個遊戲的組成,你可以參考遊戲的源碼,並試着自己也寫一遍。當然,我的源碼寫的不一定好,至少我目前覺得在遊戲各對象的耦合關係上處理地還不太好,這也是因爲開始寫的之前沒有全面規劃。

 

  到目前爲止,你通過閱讀以上全文,並付之實踐,相信一定可以開發出一個你喜歡的HTML5遊戲了。

 



四、性能優化

 

  在本節,我將分享遊戲過程中遇到的種種性能問題,和解決方案,以及一些性能測試數據。在分享這些測試數據之前,我得先說明一下我的機器配置:

  ThinkPad SL410K(屬於ThinkPad系列中最低端的一款了,傷心ing)

  操作系統:Windows 7 旗艦版

  CPU:Intel 奔騰雙核 T44 主頻2.2GHz 

  內存:4GB(標配2GB,自己加了2GB)

  硬盤:320G SAT 5400轉

  顯卡:Intel GMA X4500集顯 顯存256M

  (說明:因爲只在我一個客戶端環境中做測試,因此以下結論可能並非完全正確。)

 

  說到前端的性能優化,不得不提到《高性能JavaScript》一書,書中對網絡性能和代碼結構優化等方面都有非常全面的解說,在這裏我就不班門弄斧了,僅僅說一下在我這個遊戲中所涉及到部分內容:

 

  圖片合併:這會減少HTTP請求數,提高加載效率,你懂的。

 

  代碼合併:這會減少網絡傳輸大小,同樣用於提高加載效率,有一些工具可以快速壓縮合並你的多個腳本文件,我使用的是JsMinGUI:http://download.csdn.net/tag/JsMinGUI和JSZipper:http://download.csdn.net/download/zz2soft/3204081

 

  音頻文件壓縮:其實像我這樣的非專業人士,對音質要求並不高,好一點或差一點幾乎感覺不出來,但是如果文件大小影響到我的遊戲性能,那我就會有意見了。

  遊戲中原生的音頻格式是ogg,以遊戲中最大的音頻文件ogg_background.ogg爲例,原生ogg格式文件是578KB,在不損壞音質的情況下,轉成mp3格式是1.37M,這大小已經放大了一倍。對於網速200kb/s的用戶來說,這個我還可以接受,再加上有加載進度提示,不會對用戶體驗產生太大的影響。同樣的一個文件,在不損壞音質的情況下,轉換成wav格式,是10.7M,這比原來的ogg格式整整大了19倍,因此我無條件壓縮文件,壓縮後的大小是936kb,雖然音質上有天壤之別,但首先要保證用戶的正常操作流程。

  (關於音頻文件的格式轉換,我使用的軟件是“格式工廠”,百度一下,你就知道。)

 

  關於Canvas的渲染性能:不同的瀏覽器,Canvas渲染性能的表現差異還挺大,其中屬IE9和Chrome表現最佳(IE9這次沒有讓我失望,可能是由於硬件加速的原因)。

  我選擇了一張5.62M的圖片,尺寸爲1920* 1920,在480* 800的畫布上循環渲染1000次,各瀏覽器下測試10次後對結果取平均值得到以下數據:

 

瀏覽器      首次渲染(ms) 非首次渲染(ms)
Chrome 18 303 9
IE9 42 27
Firefox 11 1542 1250
Safari 5.1 1462 1333
Opera 11 1284 1244

  從以上測試結果中可以看出,除了Chrome和IE9,其它瀏覽器表現都差不多,而Chrome應該使用某些措施,使已渲染過的圖像在第二次渲染時,大大降低了渲染成本。而IE9的快速渲染和穩定性比較好。

 

  關於圖像渲染方式:關於圖像的渲染方式,網上有流傳說使用getImageData緩存圖片數據,在每次渲染時再使用putImageData進行渲染,性能會高於直接使用drawImage繪製;於是我準備將我程序中渲染圖形的方式進行修改,但經過親身測試,我發現各瀏覽器下表現出來的效果均不令人滿意:在不加任何邏輯代碼的情況下,僅對比putImageData和drawImage方法,putImageData相對drawImage就要慢了一倍。

  如果測試方法正確,那是我的客戶端環境問題?還是瀏覽器現在已經優化了drawImage方法的實現?或者說putImageData確實比drawImage方法要慢?於是我暫時保留了現有drawImage的渲染方式。

 

  關於渲染大圖像文件方式:如果我的背景圖片尺寸是1920*1920,但我同時只能顯示480*800一塊區域,那麼如何才能更高效地渲染呢?我們一般會這樣寫:

  第一種方式:

 

Js代碼  收藏代碼
  1. drawImage(image, -100, -200, 1290, 1920, 0, 0, 1920, 1920);  

 

 

  第二種方式:

 

Js代碼  收藏代碼
  1. drawImage(image, 100, 200, 480, 800, 0, 0, 480, 800);  

 

 

  這兩種方式除了參數不同,沒有其它區別,我們來仔細分析一下這兩種方式:

  首先是第一種方式:它將原圖片的尺寸大小原封不動地全部讀取並繪製到畫布上。

  而第二種方式,則預先計算了需要繪製的區域,從參數上看起來讀取和繪製的尺寸會更小,感覺會比第一種方式更好。但這樣做真的會得到性能上的提升嗎?

  我想做一個測試,通過數據說明兩種方式的性能差異到底有多大:和之前的測試條件一樣,但是爲了便於看到更直觀的效果,我循環了10000次,針對兩段代碼進行分別測試,測試結果爲:

 

瀏覽器      第一種方式(ms) 第二種方式(ms)
Chrome 18 74 85
IE9 2216 782
Firefox 11 14233 13426
Safari 5.1 13636 13526
Opera 11 12423 13344

  通過分析上面的測試數據,我們除了可以看到各個瀏覽器所凸顯出來的性能差別,還可以得出結論:使用drawImage渲染圖像時,參數中指定的尺寸大小並不能直接影響渲染效率和性能(除了IE9還是有一些差別,但考慮到循環次數比較多,可以評估出實際應用時性能不會受到明顯影響。)

  由此我們可以得出另一種結論:瀏覽器在調用drawImage方法渲染一個圖像時,會在內部先計算好最終渲染的位置和尺寸,對於不會被渲染到畫布上部分,即使我們調用drawImage方法時在參數中指定了這些區域,瀏覽器則不會讀取和渲染它們。

  這樣一來,我們可以放心大膽的把圖像的尺寸直接全部渲染,而不用擔心會有性能問題,更省去了一大堆用來計算位置和尺寸的邏輯。(一開始,我確實擔心這樣做會有性能問題,甚至考慮過把一張大地圖文件切分爲多個小圖,然後拼裝渲染,從這個測試結果中可以看出,這樣做是完全多餘的。當然,如果你考慮到圖像太大時的網絡加載問題,這樣做也沒有問題。)

 

  圖像平鋪渲染時的要點:我們在遊戲中經常會遇到圖像平鋪的需求,例如遊戲“超級瑪麗”中的磚頭、地面、草叢和天空等,都可以通過平鋪一張小圖來實現。

  這時我們會使用createPattern方法創建一個重複圖像的模型,再使用fillRect方法將模型填充到畫布;對於使用createPattern方法,有兩點需要注意的地方,它們可能會引起錯誤或性能下降。

  第一點:createPattern方法的第2個參數用來指定圖像的重複類型,包括repeat、repeat-x和repeat-y三種方式,這和CSS中的背景平鋪一樣。但實際上Firefox目前只支持repeat一種方式。我們必須通過填充時設定固定寬度或高度,來實現類似repeat-x和repeat-y的效果。

  第二點:我們一般會在遊戲每一幀渲染時調用fillRect方法來渲染填充模型,但創建模型的操作一定要放在初始化的時候,或者在渲染前先檢查是否已經創建過填充模型,如果沒有創建,則創建一個新的模型並把它緩存在對象的屬性中,便於下一次直接調用。因爲使用createPattern方法創建一個模型的性能開銷非常大。

  爲了解決createPattern方法的性能開銷問題,我嘗試過使用其它的一些方法來完成圖像平鋪,比如我計算好畫布的位置和尺寸,使用drawImage方法多次將圖像繪製到畫布;或者先將計算好的平鋪圖像數據存儲到一個ImageData,再使用putImageData一次性繪製到畫布。但這些做法的性能遠遠趕不上createPattern,因此只能通過其它角度去解決這個問題了。

 

  關於畫布分層渲染控制:第1次完成遊戲的初步模型時,我就發現了性能問題,在除了Chrome和IE9的其它瀏覽器下,遊戲幾乎跑不起來,經過排查,我發現這完全是由drawImage方法引起的,因爲遊戲在每更新一幀時,畫布內的所有內容都將被重繪。除去所有的邏輯代碼,僅渲染這部分操作就已經讓瀏覽器吃不消了。

  後來我決定將遊戲分爲多個Canvas來繪製,源碼幾乎全部重新寫了一遍,於是就有了現在的Layer(分層)類。一個Layer對象裏只存放相關的精靈對象,因此整個遊戲就被分成了多個層:天空層、遠景山丘層、近景山丘層、房子層、雲朵層、驢子層、效果層,這樣做讓我更容易控制它們之間的層級關係和業務邏輯。

  如果單純只是分成多個Canvas,只會增加性能的壓力;因此我給每個Layer對象增加了一個更新狀態,當Layer中的內容發生變化時,通過change方法可以改變這個狀態,狀態被改變後,Layer就會被重新渲染。如果Layer中的內容沒有發生變化,那麼它會維持當前狀態,不再重新繪製。

  這樣一來,當我遊戲中驢子在跳的時候,也許就只有驢子被重新渲染了,而云朵和天空等實際上並沒有發生變化,從而性能提升非常明顯。

  對於類似天空這樣的分層,它只會移動而不會發生其它變化,我開始嘗試使用一個DIV來做移動動畫,這樣也許會更快,但DIV確是不擅長動畫,最終的效果還是讓我放棄了這個念想。

 

  局部重繪:目前遊戲中每一幀更新,都會將Canvas清空並重繪(如果這個Layer的狀態發生變化的話),通過上面的分層方法我們已經解決了一部分性能問題,但這樣明顯還不夠,更好的方式是隻重繪畫布上被更新的一部分,如今我正在尋找一些更好的方法去實現它,如果大家已經有成熟的經驗希望也能分享一下。

 

  關於畫布的清空方式:關於畫布的清空,通過內置方法clearRect即可實現;但我們有時候也會通過重置Canvas的尺寸來實現同樣的效果,例如:

 

Js代碼  收藏代碼
  1. canvas.width = canvas.width;  

 

 

  這兩種方式的性能差別到底有多大呢?我只需要簡單測試一下就知道了,這裏我仍然使用之前的測試的條件,我會在每一次繪製後,分別使用兩種方式來清空畫布,測試結果如下:

 

瀏覽器      使用clearRect方式(ms) 通過設置尺寸方式(ms)
Chrome 18 22 67
IE9 48 261
Firefox 11 2079 3160
Safari 5.1 2236 3162
Opera 11 2092 2163

  通過以上數據可以得知,我會推薦大家使用clearRect來清空畫布,因爲它更加高效。

  還有一點值得注意的是:通過重置Canvas尺寸來清空畫布的方式,會將畫布中所有內容清空。這就意味着如果你需要實現上面提到的局部重繪效果,那麼這種清空方式是行不通的。

  (這裏所說的重置Canvas尺寸,只能通過設置畫布的width或height屬性來實現。如果你想控制樣式中的尺寸,比如canvas.style.width,那樣做只會將你的畫布內容拉伸)

 

  介紹了這麼多關於性能優化的東西,最後說一下這個遊戲的性能情況。現在我配置的幀頻是60幀/秒,這意味着遊戲每幀必須在16.6毫秒內全部更新和渲染完成,目前看來壓力並不大。在我的客戶端上,各瀏覽器平均每幀消耗的時間也各不相同:

 

瀏覽器      平均單幀時間(ms)
Chrome 18 <= 5
IE9 <= 2
Firefox 11 <= 10
Safari 5.1 <= 12
Opera 11 <= 8

  而在使用以上方法優化之前,只能跑到30幀/秒,且Firefox和Safari同時表示壓力很大;因此,我相信上面介紹的優化方法,會對大家有所幫助。

  由於我的屏幕刷新率只能支持到60幀/秒,暫時無法做一些極限測試,有資源的同學們可以試一試。



五、一些其它的東西

  開發過程中,我們會使用到各種各樣的工具;這裏把我所使用到的各種工具分享給大家,如果大家有更好的工具,也希望能分享出來。

  首先是各種瀏覽器,你懂的。

  主要編碼工具:Aptana 3.0(3.0中可以把背景設置爲黑色,看起來很酷)

  輔助編碼工具:Notepad++(選擇它,同樣是因爲它可以把背景設置爲黑色)

  代碼調試工具:firebug、Chrome和IE的開發人員工具

  性能分析工具:Firefox YSlow插件、YSlow for Chrome、IE9下使用自帶的探查器

  Web服務器環境:nginx

  HTTP請求及性能監控:HTTPWatch(實際上IE9已經不再需要)、Firebug和Chrome Network、Fiddler2

 

  寫到這裏,本文也快收尾了。整理這篇文章花費了好幾天時間,希望大家能夠多多支持。目前我已經開始下一個遊戲的整理和開發工作,在完成後,會繼續跟大家分享,希望大家多提建議,爲更多人提供一個更加優質的學習資源。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章