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://download.csdn.net/detail/ma_haile/4214215

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章