眾所周知,Java應用的執行速度雖然不慢,卻也談不上快,以最新的JRE1.6表現來說,至多也就是優勝於一些純粹的解釋型語言,距離C/C++等編譯型的執行效率還有一定差距。
平心而論,如果我們使用Java去製作一些簡單的桌面應用,那麼目前Java元件的繪圖速度勉強還能接受;但如果用Java來進行遊戲開發,尤其是製作一些需要高FPS才能滿足的動態效果,就必須利用技術手段對其加以優化,解決其繪圖效率問題,其它才有的談。
什麼是FPS?
這裡所說的FPS,並非指射擊遊戲,而是指俗稱的“幀率”(Frames per Second,縮寫:FPS)或“赫茲”(Hz)。筆者在以前的博文中曾給出過專門的解釋及Java實現示例,此處不再贅述。
受到人類眼球的生理結構制約,通常情況下只要我們所見畫面高於每秒16幀,就會認為畫面是連貫的,生物學上稱此現象為視覺暫留,這也就是為什麼電影膠片是一格一格拍攝出來,然而我們卻認為它是連續的一段。當然,所謂的16幀也只是一箇中值,並非放之四海而皆準,根據具體視覺物件不同,幀率的具體標準會有所變化。在最壞情況下,動畫不低於每秒12幀,現代電影不低於24幀,動作遊戲不低於30幀,在人眼中的畫面才是連續不間斷的。
總之,FPS數值越高則畫面流暢度便越高,越低則越顯停滯,要提高Java遊戲執行效率,那麼焦點就必然要集中在如何提高程式的FPS之上。
我們該從什麼地方入手,才能提高FPS?
Java繪圖最終要通過native,這是地球人都知道的事情。這意味著除非我們學習SWT重寫本地圖形介面,否則顯示部分我們無法干涉;這樣便決定了我們對FPS的優化必然要集中在本地圖形系統呼叫之前,而非之後。也就是說,提高Java應用的FPS,還要老生常談的從Image與Graphics及其相關子類入手。
那麼,對這些盡人皆知的Java繪圖元件,我們究竟能改進那些部分呢?
我在不久前釋出的博文《Java遊戲開發中應始終堅持的10項基本原則》中,曾經就如何構建一個高效的Java遊戲發表過一些見解,其中第7點“始終雙緩衝遊戲影像”,它不但是解決Java影像閃爍問題的關鍵所在,而且同樣是提高Java遊戲幀率的制勝法寶之一。是的,Java繪圖機能的優化與改進,關鍵就在於如何對其緩衝區域進行優化處理。
下面,我將展示三種不同的Java緩衝繪圖方式。
1、採用BufferedImage物件進行緩衝
這種方法是最簡單,同時也是最常用的雙緩衝構建方式,也就是構建一個BufferedImage緩衝當前繪圖,所有Graphics操作在其上進行,僅在需要時才將貼圖paint於窗體之上,使用上再簡單不過,但效率如何呢?文章進行到此處時尚不得而知。
2、採用BufferStrategy構建緩衝區
使用BufferStrategy構建緩衝能夠獲得系統所提供的硬體加速,Java系統會根據本地環境選擇最適合的BufferStrategy。要建立 BufferStrategy ,需要使用 createBufferStrategy() 方法告訴系統你所期望的緩衝區數目(通常使用雙緩衝,也就是填入“2”),並使用 getDrawGraphics() 方法在緩衝區之間進行交換,該方法返回下一個要使用的緩衝區。BufferStrategy最大的缺點與優點都在於其受本地圖形環境影響,既不會出現很快的圖形環境跑出很慢的FPS,也別指望很慢的圖形環境跑出很快的FPS。
3、完全在BufferedImage的DataBuffer中進行影像處理
每個BufferedImage都有一個與之對應得WritableRaster物件(getRaster方法獲得),通過它我們獲得指定BufferedImage的DataBuffer(getDataBuffer方法獲得),與方法1類似,我們同樣構建一個BufferedImage緩衝當前所有繪圖,所有操作都在其上進行,僅在需要時才將貼圖paint於窗體之上。但區別在於,由於DataBuffer可以轉化為描述BufferedImage象素點的int[],byte[]或short[]等陣列集合,因此我們不再使用Java提供的Graphics物件,而是直接操作畫素點進行所有繪圖的實現。 但是,這樣進行陣列操作會快嗎?
現在我們為其各自構建三個示例,儘量以比較趨同的處理流程構建,分別測算三種方法的具體效率。
(PS:寫完才突然想起,這臺2002年買的電腦實在不適合測試FPS(-_-|||),較新機型的FPS至少能達到此測試結果的2倍以上,特此宣告……)
(PS:以下影像素材源自成都漢森資訊科技有限公司首頁,特此宣告)
以下開始將分別對三種方法分別進行繪製1名角色、繪製100名角色、繪製1000名角色的簡單FPS繪圖效率測算。
一、採用BufferedImage物件進行緩衝