android遊戲開發(一)簡單的圖形渲染

android遊戲開發一般都是用view或surfaceView 這一章我們來學習學習 view或surfaceView 的用法 如果渲染出圖片
 
我們先看看View
   view類是android的一個超類, 這個類包含瞭所有的屏幕類型,每一個View都有一個用於繪圖的畫佈,這個畫佈可以任意擴展. 任何一個view類隻需重寫onDrae()方法來實現界面顯示。
   遊戲的核心就是不斷的繪圖和刷新界面,圖片我們通過onDraw方法來繪制,那如何來刷新界面呢, android中提供瞭ivalidate方法來實現界面刷新,註意 invalidate不能直接在線程中調用(就是不可以再子線程中調用 這個位置各位童鞋要註意喔) 因為它違背瞭android單線程模型, android ui操作並不是線程安全的,並且這些操作必須在ui線程中執行, 在平時使用中最常見的方式就是 通過 handler來實現ui線程的更新.
下面我們來看看示例 一張圖片上下移動的 效果
 這個示例有兩個類 
一個gameView 類該類需要繼承View類作為遊戲界面輸出和控制類.
一個是 MainActivity類 該類需要繼承Activity 作為程序的入口和刷新我們的視圖.
我們先看看gameView類的內容
 
package yxqz.com;
 
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
 
/**
 * android view簡單的圖片繪制 該類需要繼承view 基類
 * @author mahaile
 *
 */
public class GameView extends View {
 Bitmap bitmap_role;
 float width,height;
 float x,y;  //圖片渲染所在的屏幕的位置
 int direction=0;  //圖片運行方向 控制圖片向上 或向下運動
 /**
 * 構造方法
 * @param context
 * @param width 屏幕寬度
 * @param height 屏幕高度
 */
 public GameView(Context context,float width,float height) {
  super(context);
  this.width=width;
  this.height=height;
  //通過BitmapFactory.decodeResource 方法可以自動加載 res/drawable目錄下的圖片 R.drawable.role參數為android自動生成的圖片id
  bitmap_role=BitmapFactory.decodeResource(context.getResources(), R.drawable.role);
  x=width/2-bitmap_role.getWidth()/2;
 }
 
 @Override
 protected void onDraw(Canvas canvas) {
  // TODO Auto-generated method stub
  Log.d("GameView","GameView onDraw x is:"+x +"y is:"+y);  //Log類方法用於日志輸出
  canvas.drawBitmap(bitmap_role, x, y, null);
  super.onDraw(canvas);
 }
}
 
下面是 MainActivity 類
 
package yxqz.com;
import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
 
public class MainActivity extends Activity {
    /** Called when the activity is first created. */
 GameView gameView;
 GameSurfaceView gameSurfaceView;
 
 boolean flag; //線程標示位 當為false時停止刷新界面
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
       
       
        //通過displayMetrics獲取屏幕的寬高
        DisplayMetrics dm = new DisplayMetrics();
  this.getWindowManager().getDefaultDisplay().getMetrics(dm);
  int width = dm.widthPixels;
  int height = dm.heightPixels;
  gameView=new GameView(this,width,height);
        setContentView(gameView);
        flag=true;
        new Thread(new GameThread()).start();
    }
   
    class GameThread implements Runnable{
 
  public void run() {
   while(flag){
    if(gameView.direction==0){
     gameView.y+=0.2;
     if(gameView.y>=gameView.height){
      gameView.direction=1;
     }
    }else{
     gameView.y-=0.2;
     if(gameView.y<=0){
      gameView.direction=0;
     }
    }
    //通知UI線程重繪, gameView的主線程會自動調用onDraw的方法 這點要註意瞭 喔
    gameView.postInvalidate();
    
    //如果使用 gameView.invalidate(); 方法的話 需要需要把gameView.invalidate()寫到 handler 裡面  因為它隻支持ui主線程中使用   }
  }
  }
    }
}
 
 
上面的實例 簡單的講解瞭 android View 的使用 下面我們在看看SurfaceView 是怎麼使用的
SurfaceView的特性是:可以在主線程之外的線程向屏幕中繪圖,這樣可以避免畫圖任務繁重的時候造成主線程阻塞,從而提高程序的反應速度
有一點需要註意下surfaceView 沒有OnDraw方法重新 需要自己實現並調用
使用SurfaceView 首先需要繼承SurfaceView 並實現SurfaceHolder.Callback接口
實現 SurfaceHolder.Callback接口需要實現下面幾個接口
 
//在surface的大小改變時調用  即橫豎屏切換的時候調用
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {}
//在surface創建的時候調用  這裡一般是 啟動繪制屏幕線程
public void surfaceCreated(SurfaceHolder surfaceHolder) {}
//在surface銷毀時調用  這裡一般做資源銷毀等操作
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {}
 
SurfaceHolder是surface的控制器,用來控制surface。 處理Cavas上面的效果,動畫,控制表面,大小,像素等 
這裡需要註意幾個方法
//給SurfaceView當前持有者一個回調對象
(1) abstract void addCallback(SurfaceHolder.Callback callback);
//鎖定畫佈,一般在鎖定後可以返回畫佈對象Canvas ,在其上面畫圖操作
(2) abstract Canvas lockCanvas();
//鎖定畫佈的某個區域進行畫圖等,因為畫完圖後,會調用下面的unlockCanvasAndPost來改變顯示的內容  針對遊戲內存要求高的遊戲,可以不用重畫rect外區域的像素 可以提高速度
(3) abstract Canvas lockCanvas(Rect rect);
//結束鎖定畫佈,並提交改變
(4) abstract void unlockCanvasAndPost(Canvas canvas);
 使用 surfaceView的整個過程 
繼承SurfaceView並實現SurfaceHolder.Callback接口 –> SurfaceView.getHolder()獲得surfaceHolder對象–> SurfaceHolder.addCallback(callback)添加回調函數 –> SurfaceHolder.lockCanvas()獲得Canvas對象並鎖定畫佈 –>
Canvas繪畫–>
SurfaceHolder.unlockCanvasAndPost(Canvas canvas)–> 結束鎖定畫圖 並提交改變,將圖形顯示
 
下面我們看看 示例代碼 GameSufaceView
 
package yxqz.com;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.SurfaceHolder.Callback;

/**
 * android surfaceview簡單的圖片繪制
 * @author mahaile
 *
 */
public class GameSurfaceView extends SurfaceView implements Callback {
 
 boolean flag; //線程標示位 當為false時停止刷新界面
 SurfaceHolder surfaceHolder;
 GameViewThread gameViewThread;
 int x,y;
 int direction=0;  //圖片運行方向 控制圖片向上 或向下運動
 int width,height;
 Bitmap bitmap_role;
 public GameSurfaceView(Context context) {
  super(context);
  surfaceHolder=this.getHolder();
  surfaceHolder.addCallback(this); //添加回調
  bitmap_role=BitmapFactory.decodeResource(getResources(), R.drawable.role);
 }
 
 public void onDraw(Canvas canvas){
  canvas.drawColor(Color.BLACK);
  canvas.drawBitmap(bitmap_role, width/2-bitmap_role.getWidth()/2, y, null);
 }

 public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
  
 }

 public void surfaceCreated(SurfaceHolder surfaceHolder) {
  //獲取屏幕的 寬高 隻有在 surface創建的時候 才有效 ,才構造方法中獲取 寬高是獲取不到的
  width=this.getWidth();
  height=this.getHeight();
  //初始化繪圖線程
  gameViewThread=new GameViewThread();
  gameViewThread.flag=true;
  gameViewThread.start();
 }

 public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
  gameViewThread.flag=false; //銷毀線程
 }

 
 class GameViewThread extends Thread{
  public boolean flag;
  public void run(){
   while(flag){
    Canvas canvas=null;
    try{
     if(direction==0){
      if(y>=height){
       direction=1; 
      }
      y+=1;
     }else{
      if(y<=0){
       direction=0; 
      }
      y-=1;
     }
     canvas=surfaceHolder.lockCanvas(); //鎖定畫佈 並獲取canvas
     onDraw(canvas);//調用onDraw 渲染到屏幕
     surfaceHolder.unlockCanvasAndPost(canvas); //此步不要忘記瞭喔 否則界面上顯示不出來的
    }catch(Exception e){
     e.printStackTrace();
    }
    
    try {
     Thread.sleep(10);//線程休眠時間 單位是毫秒  控制幀數
    } catch (InterruptedException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }  //每一秒刷新一次
    
   
   }
  }
 }
}

下面看看 mainActicity代碼 程序的入口 這個就 相當簡單瞭

 
 
package yxqz.com;

import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;

public class MainActivity extends Activity {
    /** Called when the activity is first created. */
 GameSurfaceView gameSurfaceView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
  gameSurfaceView=new GameSurfaceView(this);
        setContentView(gameSurfaceView);
      
    }
 
}
 
 上面 分別列出瞭view 和surfaceView  分別跑兩個程序 發現 上面的程序中 view比surfaceView的幀數還快 這是為什麼呢, 不是說surfaceView 比view效率高嗎 ?  
  千萬不要這麼想喔,  view 對於渲染不復雜的程序 是比surfaceView 的高的 ,還有一點是 上面的程序中 沒有哪個位置 對cpu 比較消耗的, 如果對cpu 消耗比較厲害的話 surfaceView 就會比view 的效率會高的
下面是上面上個程序的源碼 ,感興趣的同學 可以 研究下  歡迎拍磚喔.. 共同學習 一起進步
http://up.aiwalls.com/2012/0417/20120417095628177.rar
 

 
摘自 android,unity3d

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *