Android遊戲開發—-動畫SurfaceView詳解

SurfaceView在遊戲開發中占著舉足輕重的地位。今天好好找資料看瞭看。附帶自己寫的例子。   
     寫變化不太快的畫面時,用View就足夠瞭,用View寫太快的畫面變化時,可能會出現屏幕閃爍。當寫如像植物大戰僵屍、水果忍者等遊戲時,用View就不能滿足要求瞭。Android提供瞭SurfaceView,它是專門用來做動畫,它是View的子類。
    在SurfaceView自帶2級緩存,當寫變換比較快的遊戲時,二級緩存會讓遊戲畫面的變化看起來比較連貫一些。2級緩存的作用就是提前把將要繪制的圖片放到內存裡面。
一、實現最基本的SurfaceView隻需基礎即可,再按4個步驟來做即可。直接上代碼,詳細看代碼裡裡面的註釋。
[java] 
public class GameView extends SurfaceView 

    SurfaceHolder surfaceHolder; 
 
    public GameView(Context context) 
    { 
        super(context); 
        //1. 獲得SurfaceView下是SurfaceHolder, surfaceHolder相當於一個遙控器來控制SurfaceView 
        surfaceHolder = this.getHolder(); 
        //這個是回調方法, 必須有. 否則會報空指針異常. 意思是surface創建, 銷毀,改變 
        surfaceHolder.addCallback(new Callback() 
        { 
 
            @Override 
            public void surfaceDestroyed(SurfaceHolder holder) 
            { 
 
            } 
 
            @Override 
            public void surfaceCreated(SurfaceHolder holder) 
            { 
                //2. 在surface創建後鎖定畫佈 
                Canvas canvas = surfaceHolder.lockCanvas(); 
                //3. 可以在畫佈上進行任意的繪畫操作( 下面是畫一條紅色 的線 ) 
                Paint paint = new Paint(); 
                paint.setColor(Color.RED); 
                canvas.drawLine(0, 0, 100, 100, paint); 
                //4. 將畫佈解鎖並顯示在屏幕上 
                surfaceHolder.unlockCanvasAndPost(canvas); 
            } 
 
            @Override 
            public void surfaceChanged(SurfaceHolder holder, int format, 
                    int width, int height) 
            { 
 
            } 
        }); 
 
    } 

當然,這樣寫很明顯不是最好的。隻有SurfaceView很明顯不能實現動畫,當然要與線程結合起來。用線程來刷屏。
二、使用SurfaceView來寫動畫的一般寫法。直接上代碼,詳細解釋請看代碼裡裡面的註釋。

[java] 
public class GameViewOK extends SurfaceView implements Callback, Runnable 

    SurfaceHolder surfaceHolder; 
    private boolean isThreadRunning = true; 
    Canvas canvas; 
    float r = 10; 
 
    public GameViewOK(Context context) 
    { 
        super(context); 
        surfaceHolder = this.getHolder(); 
        surfaceHolder.addCallback(this);//註冊回調方法 
    } 
 
    @Override 
    public void surfaceChanged(SurfaceHolder holder, int format, int width, 
            int height) 
    { 
 
    } 
 
    @Override 
    public void surfaceCreated(SurfaceHolder holder) 
    { 
        //創建surfaceView時啟動線程 
        new Thread(this).start(); 
    } 
 
    @Override 
    public void surfaceDestroyed(SurfaceHolder holder) 
    { 
        //當surfaceView銷毀時, 停止線程的運行. 避免surfaceView銷毀瞭線程還在運行而報錯. 
        isThreadRunning = false; 
        //第三種方法防止退出時異常. 當surfaceView銷毀時讓線程暫停300ms . 醒來再執行run()方法時,isThreadRunning就是false瞭.  
        try 
        { 
            Thread.sleep(300); 
        } catch (InterruptedException e) 
        { 
            e.printStackTrace(); 
        } 
    } 
 
    /**
     * 將繪圖的方法單獨寫到這個方法裡面.
     */ 
    private void drawVieW() 
    { 
        try 
        {//第一種方法防止退出時異常: 當isThreadRunning為false時, 最後還是會執行一次drawView方法, 但此時surfaceView已經銷毀 
            //因此才來判斷surfaceHolder 
            if (surfaceHolder != null) 
            { 
                //1. 在surface創建後鎖定畫佈 
                canvas = surfaceHolder.lockCanvas(); 
                //2. 可以在畫佈上進行任意的繪畫操作( 下面是畫一條紅色 的線 ) 
                Paint paint = new Paint(); 
                paint.setColor(Color.BLUE); 
                //paint.setStyle(Style.STROKE);//隻有邊框 
                paint.setStrokeWidth(5); 
                canvas.drawCircle(100, 100, r++, paint); 
            } 
        } catch (Exception e) 
        { 
            e.printStackTrace(); 
        } finally 
        { 
            //canvas是根據surfaceHolder得到的, 最後一次surfaceView已經銷毀, canvas當然也不存在瞭. 
            if (canvas != null) 
                //3. 將畫佈解鎖並顯示在屏幕上 
                surfaceHolder.unlockCanvasAndPost(canvas); 
        } 
 
    } 
 
    @Override 
    public void run() 
    { 
        //每隔100ms刷新屏幕 
        while (isThreadRunning) 
        { 
            drawVieW(); 
            try 
            { 
                Thread.sleep(100); 
            } catch (Exception e) 
            { 
                e.printStackTrace(); 
            } 
        } 
    } 
 
    /*
     * 這個是第二種方法解決退出是報錯的問題. 當按下返回鍵時, 提前設置isThreadRunning為false, 讓線程結束.
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event)
    {
        if(keyCode == KeyEvent.KEYCODE_BACK)
        {
            isThreadRunning = false;
        }
        return super.onKeyDown(keyCode, event);
    }
    */ 

運行效果:不斷增大的圓形

 

發佈留言

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