Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android游戲快速入門(二):繪制動態文字和圖片

Android游戲快速入門(二):繪制動態文字和圖片

編輯:關於Android編程

一、概述 這一講我將帶著大家來實現文字和圖片的繪制,然後試著讓文字和圖片在屏幕裡動起來。雖然,離真正的游戲還有一段距離,但是,這些都是游戲的基礎,所以,大家都是需要掌握的。好的,不多說了,一起進入正題吧!   完成這一講的任務,我們需要掌握如下一些概念,然後我會分別進行講解。 層的概念     文字層     貼圖層 View對象:自定義顯示控件     onDraw()方法:執行一系列繪制 Canvas對象:畫布,呈現數據     Paint:畫筆對象     drawText:繪制文字     drawBitmap:繪制貼圖 SurfaceView對象     SurfaceHolder.Callback Thread:線程讓畫面動起來   二、層的概念 學習photoshop的朋友肯定都知道層的概念,用通俗的話來說,層就是一個透明的玻璃紙。在android游戲中層的概念跟photoshop中也很相似,它可以用來呈現文字、圖片等元素。游戲中一般都會有很多層組成,每個層中會有不同的元素,而且每個層中的元素是獨立可控的。比如:在打飛機游戲中,背景是一層,玩家飛機是一層,敵機也是一層。大家要注意:層是有層次關系的,上面層會覆蓋下面的層。那麼,在打飛機游戲中,背景肯定是最裡面一層,其他任何游戲元素都呈現在背景的上面。   另外有一點要跟大家特別講一下,就是關於圖片素材問題,我們都知道圖片都是正規的矩形,而且有背景,所以在場景中肯定會有顏色塊,看起來很不逼真。但是PNG格式的圖片是可以做成透明背景,這樣就解決這個問題,這也就是為什麼android的圖片素材基本上都是PNG格式的原因。   文字層:顯示文字內容的層 貼圖層:顯示圖片元素的層 但是,常常文字層和貼圖層分的不是很清楚,文字層也可以繪制貼圖,貼圖層也可以繪制文字。   三、View對象 在普通的應用開發中似乎很難直接接觸到View類,但實際上幾乎所有的Android顯示組件都是繼承View類,TextView, EidtView, ImageView等等都是繼承View類。開發中我們常常在XML文件中使用這些組件,但是如果要讓組件具有更多獨特的功能就需要自定義View類來擴展我們的需求了。   在Android游戲當中充當主要的除了控制類外就是顯示類,在J2ME中我們用Display和Canvas來實現這些,而在Android中涉及到顯示的為View類,Android游戲開發中比較重要和復雜的就是顯示和游戲邏輯的處理。那麼,我們首先研究顯示的問題。   首先創建一個游戲主戰場:GameView 類,並繼承View類,結構如下: [java]   package cn.zkyc.android.game;      import android.content.Context;   import android.view.View;      public class GameView extends View {          public GameView(Context context) {           super(context);       }      }     接下來我們要將上面創建的GameView類顯示到手機屏幕上。需要在入口Activity中進行調用。項目創建的時候我就已經設定了一個主Activity,名稱為:GameStartActivity,代碼結構如下:   [java]   package cn.zkyc.android.game;   import android.app.Activity;   import android.os.Bundle;   public class GameStartActivity extends Activity {       @Override       public void onCreate(Bundle savedInstanceState) {           super.onCreate(savedInstanceState);                      // 默認是加載XML配置文件,顯示的也是XML文件中的視圖組件           //setContentView(R.layout.main);           //顯示自定義的View,只需要將XML文件換成自定義的View對象就即可,如下:           GameView gameView = new GameView(this);           setContentView(gameView);        }   }     運行Application,效果如下:   很遺憾,頁面中除了title什麼也看不到。實際上,我只是測試自定義View是否能夠正確顯示,只要程序沒有bug,就算是成功。(請看代碼中的注釋)   好的,接下來我們就在View裡面展現一些內容,這個時候就要用到View對象中的onDraw方法,在自定義的GameView對象中必須覆蓋父類View中的onDraw方法。接下來,你想展現任何內容都可以在此方法中進行了。假如,我想在屏幕的(100,100)處繪制藍色文字:“飛機大戰”,在屏幕的(100,200)處繪制一個半徑10像素的紅色圓。 [java]   @Override   protected void onDraw(Canvas canvas) {           super.onDraw(canvas);   // 畫筆對象,可以控制顏色和文字大小           Paint paint = new Paint();           // 給畫筆設置系統內置的顏色:藍色           paint.setColor(Color.BLUE);            // 文字左上角的坐標為(100,100)           canvas.drawText("飛機大戰",100, 100, paint);                      //將畫筆顏色調成紅色           paint.setColor(Color.RED);            // 圓心點坐標為(100,200)           canvas.drawCircle(100, 200, 10, paint);    }     運行效果如下圖:     到目前為止,你已經可以在自定義的GameView中繪制文字和各種圖形了,但游戲中都是大量的圖片素材,對於圖片如何繪制呢?也很簡單,Canvas類也提供了相應的drawBitmap方法。現在,我來繪制屏幕的(100,300)處繪制一個飛機圖片。只需要在ondraw方法中添加如下代碼即可: [java]  //加載資源圖片圖片   Bitmap heroBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.hero1);   // 在(100,300)處繪制圖片   canvas.drawBitmap(heroBitmap, 100, 300, paint);   運行效果如下圖:   對於代碼中還有Canvas和Paint兩個類沒有細講,我們可以這樣打個比方吧,假如Canvas是一個畫家,那麼Paint就是畫家手中的筆。畫家能夠畫出各種景象(文字,形狀,貼圖等等),就要用到不同的畫筆和不同的顏色。通過畫筆類Paint就可以調整顏色,字體樣式,字體大小等等。 我們發現繪制貼圖canvas.drawBitmap(heroBitmap, 100, 300, paint);也會用到paint對象,但實際上paint起到的作用不大,我們完全可以忽略。 這種寫法也是對的:canvas.drawBitmap(heroBitmap, 100, 300, null); 具體的用法代碼裡面已經有了,我就不再多說了,大家可以親自查詢下Android SDK API。   四、Thread:讓畫面動起來   上面我們已經實現了自定義的View中繪制了文字、形狀和貼圖,但是一切都是靜止的,跟游戲還差的很遠,意義不是很大。那麼,接下來我就帶著大家一起來讓畫面動起來。   實現這個目標,我們需要用到一個在游戲開發中非常重要的機制,就是多線程機制。具體多線程實現方式,不是我們現在討論的問題,如果還不是很明白就需要自己補補線程方面的知識了。   在這裡我們采用GameView類直接實現Runnable接口的方式: [java]  public class GameView extends View implements Runnable { }     默認必須實現run方法: [java]  public void run() {       while(true){           // 不斷的調用View中的postInvalidate方法,讓界面重新繪制           this.postInvalidate();           try {               // 暫停0.5秒繼續               Thread.sleep(500);           } catch (InterruptedException e) {               e.printStackTrace();           }       }   }     postInvalidate() :此方法是View類中的方法,功能是觸發調用onDraw方法實現界面重繪。 只要在每次重繪之前對層中對象的位置、形狀、顏色或者透明度進行修改, 而且在一秒鐘之內完成幾十次的重繪,人的眼睛根本無法分辨,所以流暢的動畫效果就產生了。動畫片和電影也是這個原理。 現在我想讓上面場景中的小球每隔0.5秒鐘改變一次透明度和顏色,飛機垂直向上飛行10dp,效果如下:   完整的代碼如下: [java]  package cn.zkyc.android.game;      import java.util.Random;      import android.content.Context;   import android.graphics.Bitmap;   import android.graphics.BitmapFactory;   import android.graphics.Canvas;   import android.graphics.Color;   import android.graphics.Paint;   import android.view.View;      public class GameView extends View implements Runnable{          Random rand = new Random();        private int dx = 0 ;   // x軸移動像素       private int dy = 0 ;   // y軸移動像素       /**       * 必須要覆蓋View中一個構造方法       * @param context       */       public GameView(Context context) {           super(context);                      //啟動線程           new Thread(this).start();       }          @Override       protected void onDraw(Canvas canvas) {           super.onDraw(canvas);           // 畫筆對象,可以控制顏色和文字大小           Paint paint = new Paint();                      // 給畫筆設置系統內置的顏色:藍色           paint.setColor(Color.BLUE);            // 文字左上角的坐標為(100,100)           canvas.drawText("飛機大戰",100, 100, paint);                      //將畫筆顏色調成紅色           //paint.setColor(Color.RED);            //ARGB : A:透明度 、R:紅色、G:綠色、B:藍色。取值范圍都在:0 ~ 255           paint.setARGB(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255), rand.nextInt(255));           // 圓心點坐標為(100,200)           canvas.drawCircle(100, 200, 10, paint);                       //加載資源圖片圖片           Bitmap heroBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.hero1);           // 在(100,300)處繪制圖片,dy值控制在y軸上的位置           canvas.drawBitmap(heroBitmap, 100, 300+dy, null);       }          @Override       public void run() {           while(true){               //修改下dy的坐標,方向向上,所以dy不斷減,每次上移10dp               dy -= 10;                // 不斷的調用View中的postInvalidate方法,讓界面重新繪制               this.postInvalidate();               //this.invalidate(); 此方法要求在UI主線程調用               try {                   // 暫停0.5秒繼續                   Thread.sleep(500);               } catch (InterruptedException e) {                   // TODO Auto-generated catch block                   e.printStackTrace();               }           }       }   }   到此,實際上我們已經完成了我們的任務,但是並沒有考慮到系統運行效率等問題。下面我將帶著大家學習一下高效且更適合做游戲開發的SurfaceView類。   五、SurfaceView對象   Surfaceview類是View類的一個子類,我們來看看API的層級關系:     1、SurfaceView的特點 可以在主線程之外的線程中向屏幕繪圖上。這樣可以避免畫圖任務繁重的時候造成主線程阻塞,從而提高了程序的反應速度   2、實現方式 定義一個游戲場景類繼承SurefaceView ,同事實現SurfaceHolder.Callback接口。因為使用SurfaceView有一個原則,所有的繪圖工作必須在Surface 被創建之後才能開始(Surface這個概念在 圖形編程中常常被提到,基本上我們可以把它當作顯存的一個映射,寫入到Surface 的內容可以被直接復制到顯存從而顯示出來,這使得顯示速度會非常快),而在Surface 被銷毀之前必須結束。所以Callback 中的surfaceCreated 和surfaceDestroyed 就成了繪圖處理代碼的邊界。   3、需要重寫的幾個方法: //在surface的大小發生改變時激發 1)  public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){} //在創建時激發,一般在這裡調用畫圖的線程。 2)  public void surfaceCreated(SurfaceHolder holder){}  //銷毀時激發,一般在這裡將畫圖的線程停止、釋放。 3)  public void surfaceDestroyed(SurfaceHolder holder) {}   4、整個代碼過程邏輯: -->繼承SurfaceView並實現SurfaceHolder.Callback接口  --> SurfaceView.getHolder()獲得SurfaceHolder對象  -->SurfaceHolder.addCallback(callback) 添加回調函數 -->SurfaceHolder.lockCanvas()獲得Canvas對象並鎖定畫布 --> Canvas繪畫  -->SurfaceHolder.unlockCanvasAndPost(Canvas canvas)結束鎖定,並提交改變,將圖形顯示。   5、SurfaceHolder 這裡用到了一個類SurfaceHolder,可以把它當成surface的控制器,用來操縱surface。處理Canvas上的效果和動畫,控制表面,大小,像素等。 幾個需要注意的方法:     // 給SurfaceView當前的持有者一個回調對象。 1)  abstract void addCallback(SurfaceHolder.Callback callback); // 鎖定畫布,一般在鎖定後就可以通過其返回的畫布對象Canvas,在其上面畫圖等操作了。 2)  abstract Canvas lockCanvas(); // 鎖定畫布的某個區域進行畫圖等,因為畫完圖後,會調用下面的unlockCanvasAndPost來改變顯示內容。 // 相對部分內存要求比較高的游戲來說,可以不用重畫dirty外的其它區域的像素,可以提高速度。 3)  abstract Canvas lockCanvas(Rect dirty); // 結束鎖定畫圖,並提交改變。 4)  abstract void unlockCanvasAndPost(Canvas canvas); 6、我們把上面View的實現功能改為SurfaceView來重新實現 [java]  package cn.zkyc.android.game;      import java.util.Random;      import android.content.Context;   import android.graphics.Bitmap;   import android.graphics.BitmapFactory;   import android.graphics.Canvas;   import android.graphics.Color;   import android.graphics.Paint;   import android.view.SurfaceHolder;   import android.view.SurfaceHolder.Callback;   import android.view.SurfaceView;      public class GameSFView extends SurfaceView implements Callback, Runnable {          private SurfaceHolder surfaceHolder;       private Random rand = new Random();       private int dx = 0, dy = 0;                 public GameSFView(Context context) {           super(context);           surfaceHolder = this.getHolder(); // 獲取SurfaceHolder對象           surfaceHolder.addCallback(this); // 添加回調       }          @Override       public void run() {                      while(true){               dy -= 10; //修改下dy的坐標,方向向上,所以dy不斷減,每次上移10dp               draw(); //調用重繪               try {                   Thread.sleep(500);               } catch (InterruptedException e) {                   // TODO Auto-generated catch block                   e.printStackTrace();               }           }       }          /**       * 自定義繪制方法       */       public void draw() {           synchronized(surfaceHolder){               // 獲取Canvas對象               Canvas canvas = surfaceHolder.lockCanvas(); // 鎖住Canvas                              //清理背景,游戲中將換成具體的背景貼圖               canvas.drawColor(Color.BLACK);                              // 畫筆對象,可以控制顏色和文字大小               Paint paint = new Paint();                  // 給畫筆設置系統內置的顏色:藍色               paint.setColor(Color.BLUE);               // 文字左上角的坐標為(100,100)               canvas.drawText("飛機大戰", 100, 100, paint);                  // 將畫筆顏色調成紅色               // paint.setColor(Color.RED);               // ARGB : A:透明度 、R:紅色、G:綠色、B:藍色。取值范圍都在:0 ~ 255               paint.setARGB(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255),rand.nextInt(255));               // 圓心點坐標為(100,200)               canvas.drawCircle(100, 200, 10, paint);                  // 加載資源圖片圖片               Bitmap heroBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.hero1);               // 在(100,300)處繪制圖片,dy值控制在y軸上的位置               canvas.drawBitmap(heroBitmap, 100, 300 + dy, null);                              surfaceHolder.unlockCanvasAndPost(canvas);  // 解鎖Canvas,更新           }                  }          @Override       public void surfaceChanged(SurfaceHolder holder, int arg1, int arg2,int arg3) {                  }          @Override       public void surfaceCreated(SurfaceHolder holder) {           // Surface創建成功啟動線程           new Thread(this).start();       }          @Override       public void surfaceDestroyed(SurfaceHolder holder) {                  }      }     大家發現,飛機移動到頂部之後就不見了,請大家思考,如何讓飛機飛過頂部之後還能從底部出來呢?  
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved