孤獨者 - 心在黑暗裡旅行 ...... ... 至尊會員
遊蕩的過客 - 枉入紅塵若許年 .. ... - UID
- 18273
- 帖子
- 12122
- 精華
- 1273
- 積分
- 240737
- 金幣
- 492641
- 威望
- 5001
- 推廣
- 0
- 閱讀權限
- 99
- 來自
- 尋覓中
- 註冊時間
- 2006-6-24
- 最後登錄
- 2015-5-27
|
1樓
大 中
小 發表於 2007-5-14 03:30
優化Java動畫編程中的顯示效果
Java動畫編程有多種實現方法,但它們實現的基本原理是一樣的,即在屏幕上畫出一系列的幀來造成運動的感覺。Java多線程技術是Java動畫編程中普遍運用的技術,它在控制動畫程序的流程和動畫的顯示效果方面起著重要的作用。Java動畫編程中的動畫閃爍和圖像殘缺不全等現象,是Java程序員經常遇到的問題。本文以作者應用實例程序為基礎,闡述如何運用多線程、重載Update、雙緩衝和圖像跟蹤等技巧來解決這類問題,以達到動畫顯示的最佳效果。
Java多線程技術
Java多線程技術簡介
目前,線程(Thread)已經為許多操作系統和應用開發系統所採用。線程是程序的單個控制流,具有順序程序的特點。但是,線程不是一個程序,它僅僅是程序的一個執行序列。線程具有很強的並發功能,在同一時刻可以有多個線程同時處於執行狀態。線程是動態的,具有一定的生命週期,分別經歷從創建、執行、阻塞、直到消亡的過程。Java語言對多線程編程的支持有兩種實現方法:一種是直接繼承Thread類,另一種是實現Runnable接口。Thread類提供了對線程的控制方法,如start(),stop(),run()、suspend()、resume()和sleep()等方法,它們可以對線程的狀態進行控制。
動畫線程的設計與實現
為了每秒中多次更新屏幕,必須創建一個線程來實現動畫的循環,這個循環要跟蹤當前幀並響應週期性的屏幕更新要求。許多Java初學者容易犯的一個錯誤是將動畫循環放在paint()中,這樣佔據了主AWT線程,而主線程將負責所有的繪圖和事件處理。因此,應該生成一個獨立的動畫線程來完成圖像的顯示和更新。例如,在一個Applet框架下,當Applet啟動(Start)時,生成一個動畫線程;在Applet停止(stop)時,終止該動畫線程以釋放它所佔用的CPU資源。下列程序代碼(簡稱「C1」代碼)是該動畫線程的具體實現:public void start() { if(animatorThread==null) { animatorThread=new Thread(this);//開始動畫線程animatorThread.start(); }}public void stop(){ //停止動畫線程 animatorThread=null;} | 上面終止動畫線程的時候,並不是調用該動畫線程的stop()方法,而是設置該動畫線程為null。因為如果直接調用線程的stop()方法會強制線程終止所有的執行工作,有時會帶來不好的結果。設置該動畫線程為null,則在run()方法中,由於不滿足循環條件,線程會自然退出。這樣,也進一步優化了該動畫程序。
重載update()和雙緩衝技術消除閃爍
在Java中,動畫發生閃爍有兩個原因:一個是由於在顯示下一幀畫面的時候,調用了repaint()方法;而repaint()方法被調用時,要清除整個背景,然後才調用paint()方法顯示畫面。這樣,在清除背景和繪製圖像的短暫時間間隔內被用戶看見的就是閃爍。另一個是由於paint()方法要進行複雜的計算,繪製每一幀花費的時間太長,圖像中的各個像素值不能同時得到,使得動畫的生成頻率低於顯示器的刷新頻率,從而造成閃爍。
下面兩種方法可以明顯地消除或減弱閃爍。
重載update()方法
當AWT接收到一個Applet的重繪請求時,它就調用Applet的update()方法。缺省情況下,update()方法清除Applet的背景,然後調用paint()方法。重載update()方法就可以將以前在paint()方法中的繪圖代碼包含在update()方法中,從而避免每次重繪時將整個區域清除。既然背景不再自動清除,Java程序員需要自己在update()中完成。
雙緩衝技術
另一種消除幀之間閃爍的方法是使用雙緩衝技術,它在許多動畫Applet中被使用。主要原理是創建一幅後台圖像,將每一幀畫入圖像,然後調用drawImage()方法將整個後台圖像一次畫到屏幕上去。這種方法的優點在於大部分繪製是離屏的。將離屏圖像一次繪至屏幕上,比直接在屏幕上繪製要有效得多。在創建後台圖像前,首先要通過調用createImage()方法生成合適的後台緩衝區,然後獲得在緩衝區做圖的環境(即Graphics類對像)。
下列實例程序代碼(簡稱「C2」代碼)就是這兩種方法的結合使用,雙緩衝技術在重載update()方法中實現。其中,offImage是Image類的對象,offGraphics是Graphics類的對象,這兩個類對象是實現雙緩衝技術的關鍵。相關代碼如下:public void paint(Graphics g){ update(g); } public void update(Graphics g){ Dimension d=getSize(); //如果後台圖像不存在,就創建一個後台圖像if((offGraphics==null)||(d.width!=offDimension.width) ||(d.height!=offDimension.height)) { offDimension=d; offImage=createImage(d.width,d.height); offGraphics=offImage.getGraphics(); } //擦除上一幀 offGraphics.setColor(getBackground()); offGraphics.fillRect(0,0,d.width,d.height); offGraphics.setColor(Color.black); //將當前的幀輸出到指定的image中 for(int i=0 ; i<10 ; i++){ offGraphics.drawImage(images,frameNumber*5%(d.width/2) ,i*d.height/10,this); } //輸出指定的後台圖像g.drawImage(offImage,frameNumber*5%(d.width/2),0,this); } | 雙緩衝技術可以使動畫平滑,但有一個缺點,要分配一個後台圖像的緩衝,如果圖像相當大,這將佔用很大一塊內存。
圖像跟蹤與程序的逐步完善
圖像跟蹤
當動畫線程剛剛啟動的時候,由於沒有全部載入圖像,屏幕上顯示的畫面經常是殘缺不全的。這時可以使用MediaTracker或ImageOberver類對像進行圖像跟蹤,待圖像全部載入後,再調用drawImage()方法將圖像輸出到屏幕上去。DrawImage()方法的第四個參數正是ImageObserver類對象,所以可以用ImageObserver類對像進行圖像跟蹤。在實際應用Applet程序的init()方法中實現圖像跟蹤,相當於在動畫線程的DrawImage()方法調用以前就畫了一次圖像,因為動畫線程的初始化過程,即init()方法是先被調用的。下列代碼(簡稱「C3」代碼)展示了init()方法使用MediaTracker類對像來實現跟蹤圖像的載入,代碼如下:public void init(){ tracker=new MidiaTracker(this); for(int i=1;i<=10;i++){ image[i-1]=getImage(getCodeBase(),"image"+i+" .gif"); //用MediaTracker類對象的addImage()方法跟蹤圖像的載入 tracker.addImage(images[i-1],0); }......} | 程序的進一步完善
在「C2」代碼的重載update()方法中加入下列if語句,從而對MediaTracker類對象的圖像跟蹤方法做出判斷,if語句如下:if(!tracker.checkAll()){ //如果圖像還沒有裝載完畢,則僅清除背景,同時輸出一個狀態 g.clearRect(0,0,d.width,d.height); g.drawString("Please wait...",0,d.height/2); return; } | 在「C1」代碼的stop()方法中加入兩行代碼,用以釋放由雙緩衝技術所佔用的內存資源,這時stop()方法改為:public void stop(){ //停止動畫線程 animatorThread=null; //釋放用於雙緩衝的內存資源 offGraphics=null; offImage=null;} | 程序修改到此,還有一個小問題,就是動畫線程啟動後,第一幅圖像有時仍有殘留痕跡,而不是隨著圖像的更新而完全擦除掉。如果想解決此問題,只要將「C2」代碼中最後的for()循環和g.drawImage()方法改為如下代碼就可以了。for(int i=0;i<10;i++){ offGraphics.drawImage(images[frameNumber%10], ,frameNumber*5%(d.width),i*d.height/10,this);}g.drawImage(offImage,0,0,this); | 保持恆定的幀速度
為了使用戶觀看動畫時沒有閃爍感,至少需要達到每秒12幀的速度。更高的幀速度會產生更平滑的動畫。通常,在動畫顯示的每兩幀之間,調用線程的sleep()方法休眠一個固定的時間。這樣做的缺點是使用絕對的延遲時間會使延遲過長,即造成等待時間過長。為了每秒顯示10~20幀圖像,並保持恆定的幀速度(也就是恆定的顯示頻率),在動畫線程的run()方法中可加入如下代碼:try{ startTime+=delay; Thread.sleep(Math.max(0,startTime-System.currentTimeMillis()));}catch(InterruptedException e){ break;} | 本文著重闡述了優化Java動畫編程的顯示效果的幾種方法。當然,隨著Java技術以及其它計算機技術的發展,還將會有多種優化動畫顯示效果的方法,例如,在提高動畫的幀速度方面,可在Java中調用微軟公司的DirectDraw工具。但是本文所介紹的方法更具有通用性。 |
|