//本文轉載自:http://www.itepub.net/pagecode/312350/2006/0428/4089.html
不久前我曾用J2ME開發了一個MotoT720下的彩色遊戲—寶石方塊(GridOne)。開發過程中積累了一些經驗,現在寫出來與大家分享。
使用雙緩衝避免螢幕閃爍
雙緩衝技術是編寫J2ME遊戲程式的關鍵技術之一。實際上,雙緩衝技術是電腦動畫的一項傳統技術。造成螢幕閃爍的主要原因在於,畫面在顯示的同時,程式又在改變它,於是畫面閃爍。解決辦法就是在記憶體中開闢一片地區作為後台畫面,程式對它更新,修改,完成後再顯示它。這樣被顯示的映像永遠是已經完全畫好的映像,程式修改的將不是正在被顯示的映像。當然還有其它方法可以解決螢幕閃爍問題,但使用雙緩衝技術是一種值得推薦的解決方案。具體方法可參見如下代碼:
public class BlocksCanvas extends Canvas implements Runnable { Graphics bg; Image buf; public BlocksCanvas() { ...... height = getHeight(); width = getWidth(); //按螢幕大小建立緩衝對象 buf = Image.createImage(width, height); //將緩衝對象的Graphics附給bg bg = buf.getGraphics(); ...... } public void run() {...... for(i=0;i<ROWS;i++) { for(j=0;j<COLS;j++) {//畫方塊 drawBlock(x,y); } } repaint(); } private void drawBlock(int block_x, int block_y) { //取得方塊的座標 int x = getLeft(block_x); int y = getTop(block_y); //取得方塊的顏色 int c= board[block_x][block_y]; bg.drawImage(imgs[c], x, y, Graphics.TOP | Graphics.LEFT); }public void paint(Graphics g) { g.drawImage(buf, 0, 0, Graphics.TOP | Graphics.LEFT); } } |
由上面代碼可見,雙緩衝思想體現在程式上就是要依次完成以下幾步工作:
1. 定義一個Graphics對象bg和一個Image對象buf,按螢幕大小建立一個緩衝對象附給buf,然後取得buf的Graphics對象附給bg。在這裡,Graphics對象可以理解為緩衝的螢幕,Image對象則可當成緩衝螢幕上的圖片。
2. 在bg(緩衝螢幕)上用drawImage()和drawString等語句畫圖,相當於在緩衝螢幕上畫圖。
3. 調用repaint()語句,它的功能是告知系統調用paint()來完成真實螢幕的顯示。這裡需要注意的是,paint()是一個系統調用語句,不能手工調用,只能通過paint()語句來調用。
4. 在paint(Graphics g)函數裡,將buf(緩衝螢幕上的圖片)畫到真實螢幕上。
以上的步驟雖然看似繁瑣,但是效果還是很不錯的。如果想要在螢幕上顯示什麼東西,只管畫在bg上,然後調用repaint()將其顯示出來就可以了。
編寫自己的斷點函數
圖1 斷點測試
在開發J2ME程式過程中,最困擾人的問題就是程式容易莫名其妙地死機。當使用JBuilder或者CodeWarrior設定斷點功能來尋找程式錯誤時,死機的機率就更大了。即使不死機,也會擔心程式受到了意外的幹擾,所以一般不推薦使用開發工具內建的斷點功能。但有時候又需要一個功能來顯示當前各變數的值,以便查錯時做出正確的判斷。於是我想了一個辦法,就是編寫自己的斷點函數。具體代碼如下:
public class BlocksCanvas extends Canvas implements Runnable { private boolean stopFlag=false;//調試標誌 ...... public void run() { //斷點位置1 testFun(“x:”+x+“y:”+y); ...... //斷點位置2 testFun(“”); ...... } private void testFun(String str) { stopFlag=true; //畫一個白色長方形 bg.setGrayScale(255); bg.fillRect(0,0, fontW, fontH); //在白色長方形上顯示str的內容 bg.setGrayScale(0); bg.drawString(str, 0,0, Graphics.TOP | Graphics.LEFT); repaint();while(stopFlag){} } public void keyPressed(int keyCode) { stopFlag=false; } } |
首先定義一個boolean類型的stopFlag變數來記錄調試標誌。一開始它的值為false,進入testFun()函數後,值設為true。顯示完str的內容後,因為stopFlag的值為true,所以while語句進入了死迴圈,程式停了下來。這時可以仔細地看清楚變數的值。然後當按下任意鍵時,keyPressed()函數捕捉到這一事件,將stopFlag設為false,死迴圈自動解開。使用此方法非常方便,只要在需要斷點的地方放置testFun()語句即可,一個程式可以放置多個testFun()語句,在迴圈語句中也可以放置testFun()語句。程式運行到testFun()語句會自動停下顯示變數值,按任意鍵程式又會自動運行,程式也不會受到意外的幹擾。圖1是調試時的。
還有一點需要說明,此方法的testFun()語句必須放在run()函數中或者run()函數運行時調用的函數中,否則就會因為while()佔用了所有CPU時間而導致keyPressed()函數無法捕捉按鍵事件,最後導致死機。
此方法只要稍加修改,就可以用做遊戲的暫停功能,而且比sleep()方法好,畢竟理論上sleep()方法不能無限期暫停下去。下面給出相應的代碼:
public class BlocksCanvas extends Canvas implements Runnable { private boolean stopFlag=false;//暫停標誌 ...... public void run() {...... testFun(); ...... }private void testFun() { while(stopFlag){} } public void keyPressed(int keyCode) { int action = getGameAction(keyCode); if(action== FIRE)stopFlag=!stopFlag; } } |
該程式段的功能為,當使用者按下FIRE鍵時,遊戲暫停;再次按下FIRE鍵,遊戲繼續運行。
(未完待續)