Android遊戲的基礎:物體運動效果

來源:互聯網
上載者:User

   在本文中,你將學習到如何在Android中實現物體運動的效果,要知道,這可是Android遊戲編程中的基礎。你將不會學到十分複雜的動畫效果,只是學習如何讓一個小球在螢幕中四處運動,小球當碰到螢幕的邊界後,將被彈回。然而,可不要小看這些基礎知識,其原理可以用在很多的遊戲情境中去。本文的讀者對象為有一定Java基礎知識及Android基礎知識的使用者。

 

   一、建立工程

  首先,我們在Eclipse中建立一個名為Movement的工程,並且選擇合適的Android SDK,如:

 

在這裡,我們選用的API是比較低的1.5版本,這樣可以讓其適應性更強。接下來,我們建立兩個類,一個是UpdateThread類,一個是SurfaceView類,它們在項目中分別是負責處理線程和畫面的兩個類,在接下來會有詳細介紹,如,分別建立這兩個類,注意選擇正確它們繼承的父類:

 

在建立完成後,系統的項目結構看上去應該象如下的樣子:

 

二、編寫Movment.java啟動程式

  任何一個Android應用都必須有一個主啟動程式來啟動,我們這裡把這個啟動程式

  命名為Movment,代碼很簡單如下:

public class Movement extends Activity {<br /> @Override<br /> public void onCreate(Bundle savedInstanceState) {</p><p> super.onCreate(savedInstanceState);<br /> setContentView(new MovementView(this));<br /> }<br />

          注意的是,我們這個啟動程式不象其他程式一樣,在啟動的時候,在setContentView中傳入介面布局檔案,而是直接將MovementView的執行個體傳遞進來,也就是說,直接啟動了MovementView這個類,在這個類中,我們將繪畫我們的小球。

 

三、介紹SurfaceView

  在Android中,SurfaceView是一個重要的繪圖容器,它可以可以直接從記憶體或者DMA等硬體介面取得映像資料。通常情況程式的View和使用者響應都是在同一個線程中處理的,這也是為什麼處理長時間事件(例如訪問網路)需要放到另外的線程中去(防止阻塞當前UI線程的操作和繪製)。但是在其他線程中卻不能修改UI元素,例如用後台線程更新自訂View(調用View的在自訂View中的onDraw函數)是不允許的。

  如果需要在另外的線程繪製介面、需要迅速的更新介面或則渲染UI介面需要較長的時間,這種情況就要使用SurfaceView了。SurfaceView中包含一個Surface對象,而Surface是可以在後台線程中繪製的。

  在本文中,我們將使用它,直接通過代碼建立一個小球,並且隨著UpdateThread線程的更新,不斷改變小球的位置,下面我們開始學習MovementView的編寫,先看下如何運用SurfaceView。

  首先匯入SurfaceView及繪圖的相關庫檔案,如下所示:

package example.movement;</p><p>import android.content.Context;<br />import android.graphics.Canvas;<br />import android.graphics.Color;<br />import android.graphics.Paint;<br />import android.graphics.Rect;<br />import android.view.SurfaceHolder;<br />import android.view.SurfaceView;<br />

 

接著,我們要繼承SurfaceView並且實現SurfaceHolder.Callback介面,這是一個SurfaceHolder的內部介面,可以實現該介面獲得介面改變的資訊,代碼如下,並且我們聲明了一些成員變數:

public class MovementView extends SurfaceView implements SurfaceHolder.Callback {<br /> private int xPos;<br /> private int yPos;</p><p> private int xVel;<br /> private int yVel;</p><p> private int width;<br /> private int height;</p><p> private int circleRadius;<br /> private Paint circlePaint;</p><p> UpdateThread updateThread;<br />}<br />

 

 

而在MovementView的建構函式中,我們設定了小球的大小和在X,Y方向上的初始座標,如下:

 

public MovementView(Context context) {<br /> super(context);<br /> getHolder().addCallback(this);</p><p> circleRadius = 10;<br /> circlePaint = new Paint();<br /> circlePaint.setColor(Color.BLUE);</p><p> xVel = 2;<br /> yVel = 2;<br />}<br />

 

接著我們來看下ondraw方法的編寫,在這裡,我們將繪畫小球,並且每次都把畫布Canvas的背景色設定為白色,以重新覆蓋之前一幀,代碼如下:

protected void onDraw(Canvas canvas) {</p><p> canvas.drawColor(Color.WHITE);</p><p> canvas.drawCircle(xPos, yPos, circleRadius, circlePaint);<br /> }<br />

我們再來看下updatePhysics這個方法如何編寫。這個方法的作用有兩個:一是處理小球的運動,二是更新小球的即時位置,因為小球在螢幕中不斷地運動,因此當小球到達比如螢幕繪畫地區的頂端後,要被彈回,因此代碼如下:

 

 

 

 

 

 

public void updatePhysics() {</p><p>//更新當前的x,y座標<br /> xPos += xVel;<br /> yPos += yVel;</p><p> if (yPos - circleRadius < 0 || yPos + circleRadius > height) {</p><p> if (yPos - circleRadius < 0) {</p><p> //如果小球到達畫布地區的上頂端,則彈回</p><p> yPos = circleRadius;<br /> }else{</p><p> //如果小球到達了畫布的下端邊界,則彈回</p><p> yPos = height - circleRadius;<br /> }</p><p> // 將Y座標設定為相反方向<br /> yVel *= -1;<br /> }<br /> if (xPos - circleRadius < 0 || xPos + circleRadius > width) {</p><p> if (xPos - circleRadius < 0) {</p><p> // 如果小球到達左邊緣</p><p> xPos = circleRadius;<br /> } else {</p><p> // 如果小球到達右邊緣</p><p> xPos = width - circleRadius;<br /> }</p><p> // 重新設定x軸座標<br /> xVel *= -1;<br /> }<br /> }<br />

 

      最後我們看下surfaceCreated這個方法的代碼,在這個方法中,主要是取得了可用的SurfaceView的地區的高度和寬度,然後設定了小球的起始座標(將其設定在螢幕的正中央位置),並且啟動了UpdateThread線程,代碼如下:

 

 

 public void surfaceCreated(SurfaceHolder holder) {</p><p> Rect surfaceFrame = holder.getSurfaceFrame();<br /> width = surfaceFrame.width();<br /> height = surfaceFrame.height();</p><p> xPos = width / 2;<br /> yPos = circleRadius;</p><p> updateThread = new UpdateThread(this);<br /> updateThread.setRunning(true);<br /> updateThread.start();<br /> }<br />

 

    此外,我們要補上surfaceChanged這個方法,這個方法意思是介面尺寸改變時才調用,在我們這個應用中並沒用到,所以我們保留為空白的方法實現:

  public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)<br /> {</p><p> }<br />

 

而surfaceDestroyed方法中,主要實現的是介面被銷毀時才調用,這裡我們停止了當前的線程所處理的任務,這裡使用了線程的join方法:

public void surfaceDestroyed(SurfaceHolder holder) {</p><p> boolean retry = true;</p><p> updateThread.setRunning(false);<br /> while (retry) {<br /> try {<br /> updateThread.join();<br /> retry = false;<br /> } catch (InterruptedException e) {</p><p> }<br /> }<br /> }<br />

 

歸納下,完整的MovementView代碼如下:

 

package example.movement;</p><p>import android.content.Context;<br />import android.graphics.Canvas;<br />import android.graphics.Color;<br />import android.graphics.Paint;<br />import android.graphics.Rect;<br />import android.view.SurfaceHolder;<br />import android.view.SurfaceView;</p><p>public class MovementView extends SurfaceView implements SurfaceHolder.Callback {</p><p> private int xPos;<br /> private int yPos;</p><p> private int xVel;<br /> private int yVel;</p><p> private int width;<br /> private int height;</p><p> private int circleRadius;<br /> private Paint circlePaint;</p><p> UpdateThread updateThread;</p><p> public MovementView(Context context) {</p><p> super(context);<br /> getHolder().addCallback(this);</p><p> circleRadius = 10;<br /> circlePaint = new Paint();<br /> circlePaint.setColor(Color.BLUE);</p><p> xVel = 2;<br /> yVel = 2;<br /> }<br /> @Override<br /> protected void onDraw(Canvas canvas) {</p><p> canvas.drawColor(Color.WHITE);<br /> canvas.drawCircle(xPos, yPos, circleRadius, circlePaint);<br /> }</p><p> public void updatePhysics() {<br /> xPos += xVel;<br /> yPos += yVel;</p><p> if (yPos - circleRadius < 0 || yPos + circleRadius > height) {<br /> if (yPos - circleRadius < 0) {<br /> yPos = circleRadius;<br /> }else{<br /> yPos = height - circleRadius;<br /> }<br /> yVel *= -1;<br /> }<br /> if (xPos - circleRadius < 0 || xPos + circleRadius > width) {<br /> if (xPos - circleRadius < 0) {<br /> xPos = circleRadius;<br /> } else {<br /> xPos = width - circleRadius;<br /> }<br /> xVel *= -1;<br /> }<br /> }</p><p> public void surfaceCreated(SurfaceHolder holder) {</p><p> Rect surfaceFrame = holder.getSurfaceFrame();<br /> width = surfaceFrame.width();<br /> height = surfaceFrame.height();</p><p> xPos = width / 2;<br /> yPos = circleRadius;</p><p> updateThread = new UpdateThread(this);<br /> updateThread.setRunning(true);<br /> updateThread.start();<br /> }</p><p> public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {<br /> }</p><p> public void surfaceDestroyed(SurfaceHolder holder) {</p><p> boolean retry = true;</p><p> updateThread.setRunning(false);<br /> while (retry) {<br /> try {<br /> updateThread.join();<br /> retry = false;<br /> } catch (InterruptedException e) {<br /> }<br /> }<br /> }<br />}<br />

 

四、編寫UpdateThread

  下面,我們開始著手編寫UpdateThread線程程式。這個程式主要是啟動一個線程去不斷更新當前小球的位置。先看聲明及建構函式部分:

 

package licksquid.movement;</p><p>import android.graphics.Canvas;<br />import android.view.SurfaceHolder;</p><p>public class UpdateThread extends Thread {<br /> private long time;<br /> private final int fps = 20;<br /> private boolean toRun = false;<br /> private MovementView movementView;<br /> private SurfaceHolder surfaceHolder;</p><p>}<br />public UpdateThread(MovementView rMovementView) {<br /> movementView = rMovementView;<br /> surfaceHolder = movementView.getHolder();<br /> }<br /> public void setRunning(boolean run) {<br /> toRun = run;<br /> }<br />

 

 

 

注意這裡的setRunning方法中設定了線程是否應該停止的標記,下面來看重要的方法run:

 

public void run() {</p><p> Canvas c;<br /> while (toRun) {</p><p> long cTime = System.currentTimeMillis();</p><p> if ((cTime - time) <= (1000 / fps)) {</p><p> c = null;<br /> try {<br /> c = surfaceHolder.lockCanvas(null);</p><p> movementView.updatePhysics();<br /> movementView.onDraw(c);<br /> } finally {<br /> if (c != null) {<br /> surfaceHolder.unlockCanvasAndPost(c);<br /> }<br /> }<br /> }<br /> time = cTime;<br /> }<br /> }<br />

 

在run方法中,主要實現了如下幾個任務:首先檢查是否有允許啟動該線程(在開始運行後,由於在MovementView中,啟動UpdateThread的時候,已經設定了其值為true,即updateThread.setRunning(true)),接下來檢查是否在指定的時間內(這裡設定的是每秒20幀),如果是的話,則調用surfaceHolder的lockCanvas方法,鎖定當前的畫布繪畫地區,並且調用movementView的updatePhysics方法及onDraw方法去畫小球並判斷小球的運動,最後記得要在finally中調用unlockCanvasAndPost方法。

  五、運行程式

  最後運行程式,可以看到如下的效果,可以看到小球在做各個方向的彈跳運動。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.