最佳化Java動畫編程中的顯示效果

來源:互聯網
上載者:User

 

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[i],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工具。但是本文所介紹的方法更具有通用性。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.