摘要:為減少J2ME游戲運行平臺的硬件環(huán)境對游戲的性能限制,在開發(fā)階段必須對游戲的程序進行改良化設(shè)計和采用優(yōu)化算法。文章從游戲運行性能的角度出發(fā),分析了各種常用方案的不足,并給出了對象池技術(shù)、基本數(shù)據(jù)類型替換法、屏蔽函數(shù)計算三種能夠節(jié)省資源開銷和處理器時間以提高游戲運行性能的優(yōu)化策略。實驗對比表明,采用優(yōu)化算法后性能得到了提高。
關(guān)鍵詞:手機游戲;J2ME;MIDP;性能;優(yōu)化
0 引言
傳統(tǒng)手機軟件是由手機開發(fā)廠商固化在手機中的。在SUN公司推出J2ME后,各大手機廠商很快就推出了支持J2ME的手機。支持J2ME的手機可運行由第三方提供的基于J2ME開發(fā)的軟件,使手機的擴展功能得到極大增強。
在J2ME規(guī)范之中,J2ME定義了基于Java類庫的CDC(Connected Device Configuration)和CLDC(Connected LimitedDevice Configuration),在CDC和CLDC之上J2ME又定義了Profile層,稱之為MIDP(Mobile Information Device Profile)。MIDP對CDC/CLDC進行和一定程度的封裝,并定義了一套整應(yīng)用程序接口和用戶接口。MIDP為J2ME應(yīng)用提供了兩類UI(User Interface),分別稱做高級用戶界面和低級用戶界面。高級用戶界面是被適配到設(shè)備上由手機操作系統(tǒng)定義外觀的通用圖形組件;低級用戶界面則允許開發(fā)者根據(jù)需要在界面上任意繪制圖形,是由開發(fā)者完全控制顯示內(nèi)容的圖形界面。由于手機游戲界面絕大多數(shù)由自定義的圖形元素構(gòu)成,所以不可避免地要采用低級用戶界面。
低級用戶界面開發(fā)具有高度的自由性,不同的游戲架構(gòu)和不同的編碼風格將對最終產(chǎn)品的性能產(chǎn)生極大影響。目前對于J2ME手機游戲開發(fā)而言,最大的問題在于J2ME運行平臺有限的資源。如何有效地利用現(xiàn)有資源以提高游戲的運行性能,將成為開發(fā)者面臨的首要問題。本文將以一款飛行射擊游戲為例,探討其中三種優(yōu)化方案對手機游戲帶來的性能改善。
1 J2ME游戲運行性能優(yōu)化準則
目前被普遍采用的優(yōu)化方案有:
(1)優(yōu)化循環(huán),通過將重復(fù)的子表達式重新組織來提高循環(huán)體的運行性能;
(2)減少使用對象的數(shù)量來提高運行性能;
(3)縮減網(wǎng)絡(luò)傳輸數(shù)據(jù)來縮短等待時間等等。
本文給出了三種性能優(yōu)化的策略:
(1)采用對象池技術(shù),提高對象的利用率;
(2)局部使用基本數(shù)據(jù)類型代替對象,節(jié)省資源開銷;
(3)用簡單的數(shù)值計算代替復(fù)雜的函數(shù)計算,節(jié)省處理器時間。
1.1采用對象池技術(shù),提高對象的利用效率
Java是面向?qū)ο蟮木幊陶Z言,創(chuàng)建和釋放對象會占用相當大的資源,而在Java里不用對象在很多情況下又無法實現(xiàn)。本文提出一種對象池技術(shù),將有效解決創(chuàng)建和釋放對象帶來的性能損失問題。
例如游戲中敵機的處理方式:一種解決方案是游戲在載入關(guān)卡的時候,為每架敵機創(chuàng)建一個對象,隨著游戲的行進,按照游戲進程顯示不同的敵機。這種方案中創(chuàng)建對象的資源開銷巨大,因此嚴重影響手機游戲的運行性能。雖然在敵機被擊毀的時候可以將對應(yīng)的對象設(shè)置為1并由System.gc()回收,但在下一關(guān)卡載入的時候還要重新創(chuàng)建相關(guān)的對象,增加了用戶的等待時間。另一種方案是在游戲的進程中,根據(jù)需要動態(tài)創(chuàng)建敵機對象,被擊毀后將對象設(shè)置為1并由System.gc()回收。這種方案雖然能減少游戲載入時間,但是頻繁地創(chuàng)建和釋放對象的資源開銷使游戲變得不流暢,對于射擊游戲這類對實時性要求很高的游戲而言,這一點是不可接受的。
從研究數(shù)據(jù)來看,游戲性能損耗主要源之于創(chuàng)建和釋放對象,而不創(chuàng)建對象又無法實現(xiàn)邏輯功能,因此要盡量避免對象的創(chuàng)建和釋放。問題的重點就轉(zhuǎn)到怎樣有效利用已有的對象上。本文提出的對象池技術(shù),就是根據(jù)需求先創(chuàng)建一定量的對象,在需要創(chuàng)建對象的時候從池中申請空閑對象,釋放對象時把對象釋放回池中,以有效避免由創(chuàng)建和釋放對象帶來的性能損失。
分析游戲需求發(fā)現(xiàn)同時顯示的敵機數(shù)量最多不過5架,采用對象池技術(shù)可以先定義一個對象池,容量為同時顯示的敵機的最大數(shù)量:
Enemy[5]enemy=new Enemy[5];
for(int i=0;i<5:i++){
enemy[i]=new Enemy();
}
在類Enemy里增加標志屬性used和帶參數(shù)的reset方法使對象可重置到初始狀態(tài),在載入游戲關(guān)卡的時候初始化對象池,在需要創(chuàng)建對象的時候從對象池獲取一個未被使用的對象并使用reset方法初始化,需要釋放對象的時候只需將標志位修改以供下次使用。與第一種解決方案相比使用對象池減少了相當大一部分的資源開銷,與第二種解決方案相比使用對象池避免了頻繁創(chuàng)建對象的額外資源開銷。
表1為索尼愛立信T618手機真機運行時的載入時間對比和游戲幀數(shù)(FPS)對照表,其中取1000除以每個gameloop的結(jié)束時間減去開始時間的差的商,即一秒內(nèi)gameloop的運行次數(shù)為fps參考值。
表1 游戲載入時間對照表
實際運行結(jié)果:未優(yōu)化前,在索尼愛立信T618手機上真機測試,載入時間達到17秒之多;優(yōu)化后,時間縮短到3秒。雖然方案2的載入時間更為短暫但是游戲運行情況不流暢,嚴重影響用戶體驗。由此可見,使用對象池可以有效減少申請和釋放對象所產(chǎn)生的損失,從而提高游戲的運行性能。
1.2盡可能地使用基本數(shù)據(jù)類型代替對象
對象雖然屏蔽了細節(jié)實現(xiàn),但是是以犧牲存儲空間來實現(xiàn)的。使用基本數(shù)據(jù)類型則僅需要少量的存儲空間。雖然對象在邏輯的實現(xiàn)上具有優(yōu)勢,但是在絕大多數(shù)情況下,使用基本數(shù)據(jù)類型比使用對象更高效,所以在局部不影響邏輯的情況下可以考慮用基本數(shù)據(jù)類型代替對象實現(xiàn)。
在游戲中飛機要發(fā)射子彈,基于面向?qū)ο蟮脑O(shè)計模式可將子彈抽象成Bullet類,然后定義代表子彈的對象池,在發(fā)射子彈的時候,在對象池中查找可用對象并重置其位置為飛機所在的位置,即在每一個gameloop中將Bullet對象的縱坐標值減少一定數(shù)值(屏幕坐標系Y軸坐標為自上而下遞增)。由于對象操作的效率低于對基本數(shù)據(jù)類型的操作,由此可以想到由基本數(shù)據(jù)類型來代替對象實現(xiàn)。代碼如下:
int[][] bullet=new int[2][10]://假設(shè)同屏同時顯示10顆子彈
for(int i=0;i<10;i++){
bullet[0][i]=999;//對應(yīng)Bullet對象的xposition屬性
bullet[1][i]=999;//對應(yīng)Bullet對象的yposition屬性
}
在每一個gameloop中:
while(run){
for(int i=0;i<10:i++){
if(bullet[1][i]==999){
//,重置為發(fā)射子彈飛機當前的位置
bullet[0][i]==myPlane.getXposition();
bullet[1][i]==myPlane.getYposition();
}else{
//,當前使用的子彈讓其縱坐標減少
bullet[1][j]一=10:
}
}
}
表2是飛機單獨發(fā)射子彈的情況下的游戲幀數(shù)對照表。同時在索尼愛立信K700c上運行相同游戲以作性能對比參照。
表2 游戲運行幀數(shù)對照表
實際運行結(jié)果證明:在發(fā)射子彈的時候,如果用對象來實現(xiàn),畫面明顯遲滯,而用數(shù)組實現(xiàn)則不會發(fā)生遲滯。由此可見使用數(shù)組代替對象將有助于性能的提升。
1.3用簡單的數(shù)值計算代替復(fù)雜的函數(shù)計算
在任何一款游戲里,都不可避免地要進行函數(shù)運算,而大量復(fù)雜的函數(shù)計算勢必會占用大量的空間和處理器時間,進而影響游戲性能。減少函數(shù)計算復(fù)雜度或者減少復(fù)雜函數(shù)的調(diào)用次數(shù)甚至避免使用復(fù)雜函數(shù)計算,將有利于游戲性能的提高。與復(fù)雜函數(shù)運算相比,簡單的數(shù)值計算無論從時間上還是空間上都有極大的優(yōu)勢。
在游戲中敵機也會發(fā)射子彈,不同于我機子彈,敵彈的運行軌跡可以朝著任意一個方向。計算敵彈運行軌跡常用的方法是使用三角函數(shù),根據(jù)飛行方向和橫坐標軸正方向的夾角來計算敵彈的位移量。雖然MIDP2.0提供了三角函數(shù)的類庫,但是在每個gameloop中計算每顆敵彈的運行軌跡勢必有大量的三角函數(shù)運算而導(dǎo)致性能損失,當子彈數(shù)目變大時這種影響更加明顯。同時由于MIDPI.0并不提供三角函數(shù)類庫,所以該游戲無法運行在不支持MIDP2.0的手機上。
由基本的數(shù)學(xué)原理可知,當角度一定時其對應(yīng)的三角函數(shù)值也是一定的,又因為三角函數(shù)是周期函數(shù),因此本文的優(yōu)化算法是用0°-45°的函數(shù)值通過簡單的四則運算來模擬任意函數(shù)值。實現(xiàn)方式如下:
首先定義如下數(shù)組
public static int iAngle[][]=new int[][]{
//根據(jù)游戲需要定義不同的精度,可以從0連續(xù)定義到45度。
因為SinX=Cos(90-X),所以不必重復(fù)定義46度到90度的函
數(shù)值。
{0,1000,0},
{5,996,87},
{10,985,174),
{40,766,643},
}45,707,707}
}
無論子彈朝著哪個方向飛行,假設(shè)飛行速度一定,其橫縱坐標位移量均為該方向?qū)?yīng)的余弦和正弦值,所以可以重新定義敵機子彈數(shù)組,將位移量包含到數(shù)組中。本游戲設(shè)定每顆子彈的飛行方向始終是固定不變的,即不會出現(xiàn)弧型的飛行軌跡:
int[][] enemyBullet=new int[4][20];
//假設(shè)同屏同時顯示20顆敵機子彈
int size=enemyBultet.length;
for(int i=0:i enemyBullet[0][i]=999;//對應(yīng)敵機子彈對象的×坐標 enemyBullet[1][i]=999;//對應(yīng)敵機子彈對象的y坐標 enemyBullet[2][i]=999; //對應(yīng)敵機子彈對象的運行時的×坐標位移量 enemyBullet[3][i]=999; //對應(yīng)敵機子彈對象的運行方的y坐標位移量 } 按照程序的需要,從數(shù)組iAngle中把特定的數(shù)值賦值到enemyBullet[2][i]和enemyBullet[3][i],而在每一個gameloop中只需要執(zhí)行如下代碼: inc size=enemyBullet.length: for(int i=0;i if(enemyBullet[0][i]!=999enemyBullet[1][i]!=999){ enemyBullet[0][i]+=enemyBullet[2][i]; enemyBullet[1][i]+=enemyBullet[3][i]; } } 以上過程避免了每個gameloop中使用三角函數(shù)計算子彈的位移,同時也使MIDPI.0平臺的手機不會因為不支持MIDP2.0而無法運行程序。為了保證足夠的精度,這里相關(guān)函數(shù)值取3位小數(shù),在處理時可以將橫縱坐標值enemyBullet[0][i:和enemyBullet[1][i]定義成坐標值的1000倍,然后與坐標值enemyBullet[2][i]和enemyBullet[3][i]相加,顯示的時候?qū)nemyBullet[0][i]和enemyBullet[1][i]的值除以1000取整。表3為優(yōu)化前后的運行情況對照表。 表3 游戲運行幀數(shù)對照表 實際運行結(jié)果表明,在敵機一次性發(fā)射20顆子彈的情況下,若不使用優(yōu)化方案,索尼愛立信T618由于不支持MIDP2.0而無法進入游戲。在使用優(yōu)化后的方案后,索尼愛立信T618能進入游戲并有最高20fps的幀數(shù)。若使用索尼愛立信K700c作為測試環(huán)境,將敵機子彈數(shù)量增加到100顆也能流暢運行,加上一些“彈幕”特效算法,已經(jīng)基本上能實現(xiàn)彩京公司出品的街機游戲《打擊者1945》中大部分效果。 2 結(jié)束語 當前J2ME游戲的性能的瓶頸在于有限的存儲資源和偏弱的處理器速度,而J2ME游戲優(yōu)化的關(guān)鍵之處在于節(jié)省資源開銷、節(jié)省冗余計算和計算簡化,所以犧牲部分設(shè)計上的結(jié)構(gòu)化(即面向?qū)ο?換來性能的提升,在當前情況下仍然是非常有必要的。本文提出的優(yōu)化方法為在較低的硬件條件下實現(xiàn)流暢的畫面,提供了可行的解決方案。 注:本文中所涉及到的圖表、注解、公式等內(nèi)容請以PDF格式閱讀原文。