文章目錄
在開始實際的遊戲迴圈之前,讓我們先顯示一些圖片,使得我們能夠得到一些尺寸的概念(不太清楚這句什麼意思)。如果還沒有看到線程更新螢幕的內容,強烈推薦先看一下(上篇部落格)
在android上面顯示圖片非常的簡單
為了讓問題簡單,我們在左上方顯示圖形,我們需要 一個圖片,我更喜歡png,我建立了一個名為droid_1.png的檔案,大小是20*20 像素,你可以選擇自己喜歡的工具,我用gimp或者ps
為了讓程式可以使用,把圖片拷貝到/res/drawable-mdpi目錄下,我選擇mdpi,它的意思是平常螢幕 中等密度,關於螢幕類型,可以求助 android文檔
修改MainGamePanel檔案,修改onDraw函數
1 protected void onDraw(Canvas canvas) { 2 canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.droid_1), 10, 10, null); 3 }
drawbitmap把droid_1畫到了座標10,10位置上
我們傳遞圖片id給程式的資源管理部分以獲得bitmap,當我們拷貝png到資來源目錄時候,外掛程式會自動在R.java裡面產生標識
也影響了線程的一部分code,檢查下面的run函數
01 public void run() { 02 Canvas canvas; 03 Log.d(TAG, "Starting game loop"); 04 while (running) { 05 canvas = null; 06 // try locking the canvas for exclusive pixel editing on the surface 07 try { 08 canvas = this.surfaceHolder.lockCanvas(); 09 synchronized (surfaceHolder) { 10 // update game state 11 // draws the canvas on the panel 12 this.gamePanel.onDraw(canvas); 13 } 14 } finally { 15 // in case of an exception the surface is not left in 16 // an inconsistent state 17 if (canvas != null) { 18 surfaceHolder.unlockCanvasAndPost(canvas); 19 } 20 } // end finally 21 } 22 }
第二行 我們聲明了我們要畫圖形的畫布,畫布是surface的圖形要畫的地方,也是我們編輯像素的地方,8行我們得到了畫布,12行我們觸發了onDraw函數,並把畫布傳遞過去,注意這是同步塊,別人是無法使用的。
這個函數很簡單和基礎,每次執行的時候,遊戲迴圈得到畫布,傳遞給game panel去畫東西,game panel把圖片畫到座標10,10處。回到FPS,如果每秒圖片顯示的次數低於20,就會被人注意到,我們的挑戰就是保持到一定的水平以上,很快我們就會看到
運行一下代碼,我們看到droid在左上方顯示了
Droid in top left corner
移動圖形
我們已經顯示圖形了,讓我們移動它,怎麼移動呢?用我們的手指。 我們會實現一個拖放的操作。為了選擇一個圖片,我們只是觸摸它,當我們的手指還在螢幕的時候,我們的也會更新圖片的座標,當觸摸接觸,圖片會放到最後觸摸的位置
我們需要建立一個對象來儲存圖片和座標
我建立了Droid.java,放到了net.obviam.droidz.model包下
01 package net.obviam.droidz.model; 02 03 import android.graphics.Bitmap; 04 05 public class Droid { 06 07 private Bitmap bitmap; // the actual bitmap 08 private int x; // the X coordinate 09 private int y; // the Y coordinate 10 11 public Droid(Bitmap bitmap, int x, int y) { 12 this.bitmap = bitmap; 13 this.x = x; 14 this.y = y; 15 } 16 17 public Bitmap getBitmap() { 18 return bitmap; 19 } 20 public void setBitmap(Bitmap bitmap) { 21 this.bitmap = bitmap; 22 } 23 public int getX() { 24 return x; 25 } 26 public void setX(int x) { 27 this.x = x; 28 } 29 public int getY() { 30 return y; 31 } 32 public void setY(int y) { 33 this.y = y; 34 } 35 }
這是個只有幾個屬性和一個建構函式的簡單類
droid的x和y座標,還有要顯示的bitmap
還沒有什麼特殊的,但是為了要運行起來,我們需要增加一些狀態,為了保持簡單,droid就有倆狀態,被觸摸和麼有被觸摸,觸摸 就是說手指在螢幕上按著droid,按到droid的時候我們就保持觸摸狀態為真,否則就是假了
看一下新的droid的類
1 package net.obviam.droidz.model; 02 03 import android.graphics.Bitmap; 04 import android.graphics.Canvas; 05 import android.view.MotionEvent; 06 07 public class Droid { 08 09 private Bitmap bitmap; // the actual bitmap 10 private int x; // the X coordinate 11 private int y; // the Y coordinate 12 private boolean touched; // if droid is touched/picked up 13 14 public Droid(Bitmap bitmap, int x, int y) { 15 this.bitmap = bitmap; 16 this.x = x; 17 this.y = y; 18 } 19 20 public Bitmap getBitmap() { 21 return bitmap; 22 } 23 public void setBitmap(Bitmap bitmap) { 24 this.bitmap = bitmap; 25 } 26 public int getX() { 27 return x; 28 } 29 public void setX(int x) { 30 this.x = x; 31 } 32 public int getY() { 33 return y; 34 } 35 public void setY(int y) { 36 this.y = y; 37 } 38 39 public boolean isTouched() { 40 return touched; 41 } 42 43 public void setTouched(boolean touched) { 44 this.touched = touched; 45 } 46 47 public void draw(Canvas canvas) { 48 canvas.drawBitmap(bitmap, x - (bitmap.getWidth() / 2), y - (bitmap.getHeight() / 2), null); 49 } 50 51 public void handleActionDown(int eventX, int eventY) { 52 if (eventX >= (x - bitmap.getWidth() / 2) && (eventX <= (x + bitmap.getWidth()/2))) { 53 if (eventY >= (y - bitmap.getHeight() / 2) && (y <= (y + bitmap.getHeight() / 2))) { 54 // droid touched 55 setTouched(true); 56 } else { 57 setTouched(false); 58 } 59 } else { 60 setTouched(false); 61 } 62 63 } 64 }
我們加入了touched來記錄droid的狀態
看一下MainGamePanel,變了可不少呢。
001 package net.obviam.droidz; 002 003 import net.obviam.droidz.model.Droid; 004 import android.app.Activity; 005 import android.content.Context; 006 import android.graphics.BitmapFactory; 007 import android.graphics.Canvas; 008 import android.graphics.Color; 009 import android.util.Log; 010 import android.view.MotionEvent; 011 import android.view.SurfaceHolder; 012 import android.view.SurfaceView; 013 014 public class MainGamePanel extends SurfaceView implements 015 SurfaceHolder.Callback { 016 017 private static final String TAG = MainGamePanel.class.getSimpleName(); 018 019 private MainThread thread; 020 private Droid droid; 021 022 public MainGamePanel(Context context) { 023 super(context); 024 // adding the callback (this) to the surface holder to intercept events 025 getHolder().addCallback(this); 026 027 // create droid and load bitmap 028 droid = new Droid(BitmapFactory.decodeResource(getResources(), R.drawable.droid_1), 50, 50); 029 030 // create the game loop thread 031 thread = new MainThread(getHolder(), this); 032 033 // make the GamePanel focusable so it can handle events 034 setFocusable(true); 035 } 036 037 @Override 038 public void surfaceChanged(SurfaceHolder holder, int format, int width, 039 int height) { 040 } 041 042 @Override 043 public void surfaceCreated(SurfaceHolder holder) { 044 // at this point the surface is created and 045 // we can safely start the game loop 046 thread.setRunning(true); 047 thread.start(); 048 } 049 050 @Override 051 public void surfaceDestroyed(SurfaceHolder holder) { 052 Log.d(TAG, "Surface is being destroyed"); 053 // tell the thread to shut down and wait for it to finish 054 // this is a clean shutdown 055 boolean retry = true; 056 while (retry) { 057 try { 058 thread.join(); 059 retry = false; 060 } catch (InterruptedException e) { 061 // try again shutting down the thread 062 } 063 } 064 Log.d(TAG, "Thread was shut down cleanly"); 065 } 066 067 @Override 068 public boolean onTouchEvent(MotionEvent event) { 069 if (event.getAction() == MotionEvent.ACTION_DOWN) { 070 // delegating event handling to the droid 071 droid.handleActionDown((int)event.getX(), (int)event.getY()); 072 073 // check if in the lower part of the screen we exit 074 if (event.getY() > getHeight() - 50) { 075 thread.setRunning(false); 076 ((Activity)getContext()).finish(); 077 } else { 078 Log.d(TAG, "Coords: x=" + event.getX() + ",y=" + event.getY()); 079 } 080 } if (event.getAction() == MotionEvent.ACTION_MOVE) { 081 // the gestures 082 if (droid.isTouched()) { 083 // the droid was picked up and is being dragged 084 droid.setX((int)event.getX()); 085 droid.setY((int)event.getY()); 086 } 087 } if (event.getAction() == MotionEvent.ACTION_UP) { 088 // touch was released 089 if (droid.isTouched()) { 090 droid.setTouched(false); 091 } 092 } 093 return true; 094 } 095 096 @Override 097 protected void onDraw(Canvas canvas) { 098 // fills the canvas with black 099 canvas.drawColor(Color.BLACK); 100 droid.draw(canvas); 101 } 102 }
Line 28 creates the droid
object at the the coordinates
50,50.
It is declared as an attribute in line 20.
In the onTouchEvent
(method line 71) if the action is the touch of the screen (MotionEvent.ACTION_DOWN
) we want to know if our finger landed on the droid. To do this is easy. We need to check if the event’s coordinates
are inside the droid’s bitmap. In order not to clutter the onTouch
event we just delegate this to the droid object. Now you can go back to the
Droid.java
class and check the handleActionDown
method.
28行 建立了droid對象,在座標50,50
在onTouchEvent方法中,如果觸控螢幕幕的動作,我們確認是否在droid上面,就是看看觸摸事件的座標是否在圖形的座標內。為了不讓onTouchEvent雜亂,我們把這個放到droid對象中(其實我覺得在droid內部也可以,要是droid類是怎麼設計的),現在回到droid.java,看一下handleActionDown方法。
public void handleActionDown(int eventX, int eventY) {if (eventX >= (x - bitmap.getWidth() / 2) && (eventX <= (x + bitmap.getWidth()/2))) {if (eventY >= (y - bitmap.getHeight() / 2) && (y <= (y + bitmap.getHeight() / 2))) {// droid touchedsetTouched(true);} else {setTouched(false);}} else {setTouched(false);}}
很簡單,如果在droid內部,把touched狀態設成true。
回到onTouched方法,看一下MotionEvent.ACTION_MOVE,如果droid是touched,我們更新一下它的座標。
而ondraw函數 就是把droid畫到surface上。
就是這樣