Android遊戲開發之SurfaceView與遊戲開發

Android中SurfaceView,是不使用遊戲引擎,開發一款遊戲的常用view控件。

SurfaceView常常被用來顯示那些更新速度比較快(這個速度通常人眼無法識別)的圖像,常常被用來顯示照相機的當前效果,視頻的播放,遊戲界面的播放。

SurfaceView的創建:

繼承SurfaceView就可以使用構造方法創建了。

所要從新寫的方法也只不過一個構造方法,這裏使用的構造常常有兩種,一種使用只有一個參數的構造方法,參數爲context,如果使用這樣的構造方法,則創建SurfaceView的Activity不能加載layout文件夾中的xml文件,只能加載new出來的view如

public class GameMainActivity extends Activity {
    public static GameView Game;
    public static AssetManager assets;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        Assets.mContentResolver=getContentResolver();
       
        Game=new GameView(GameMainActivity.this);
        setContentView(Game);
      
       assets=getAssets();
    }
}

這裏的GameView繼承自SurfaceView

如果使用兩個參數的構造方法:

Context context, AttributeSet attrs
則可以將自己View類當做Activity佈局文件中的一個空間,當佈局文件顯示時會自動調用這個構造方法,第二參數用於返回相應的指令給View。

但是這裏建議使用第一個方法構造,因爲在遊戲中的界面跳轉與外部Activity的跳轉邏輯完全不同,所以在佈局文件中加載會有一定的弊端。但是好處是這樣可以使用Android封裝好的控件(如button)。


SurfaceView有一個特點:

他並不是當Activity創建 就會可以調用的,裏面的那些控件,並不是真的控件,當Activity創建完成就可以使用了,而是當SurfaceView創建,並出現在屏幕上之後纔可以開始繪畫(即僞控件的加載)。

這時就需要一個監聽器監聽SurfaceView的創建,改變和銷燬。

使用監聽器

SurfaceHolder.Callback
這裏要實現這個接口,重寫其中的三個主要方法:

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) 
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) 
public void surfaceDestroyed(SurfaceHolder holder) 
這三個方法代表了SurfaceView生命週期,注意只有在這三個聲明週期中纔可以進行其中的操作,在Activity中的操作要與之區分。

當創建時自動調用createed和changed方法,在home鍵,back鍵返回時調用destroyed方法,但是注意home鍵返回再回來依次調用created方法,和changed方法,但是back鍵返回再回來就會調用構造方法和created方法,changed方法。
設置監聽時要先初始化SurfaceView的管家類SurfaceHolder,通過在View中調用父類SurfaceView的方法getholder就可以簡單的獲取管家。
再使用holder.addCallBack()進行設置監聽聲明週期

SurfaceView的繪畫,一般在線程中進行繪畫操作。
繪畫之前要得到View的主畫布,通過holder來實現,這時體現瞭如果線程在View類中就可以很好地得到主畫布,所以GameView通常可以實現Runnable接口。
Canvas的得到使用holder.lockCanvas()的返回值得到,得到主畫布後就可以使用canvas中的各種繪畫方法進行繪畫了。
如果想不斷地更新,就要使用循環,也就是死循環進行一遍一遍地render和update進行繪畫,與邏輯更新,這裏就是遊戲主循環。

SurfaceView的事件監聽使用OnTouchListener和MotionEvent,這裏由於不能使用Android的諸多控件,只能使用這種觸控事件。諸多組件也是畫上去的再通過座標的對比,進行事件的響應。
這裏由於遊戲代碼所涉及的類諸多,只展示一個Demo,一個畫板的實現(沒有適配不同機型)
public class MyView extends SurfaceView implements Runnable,SurfaceHolder.Callback,View.OnTouchListener {
    Context main;
    SurfaceHolder holder;//主holder
    Canvas canvas;//主畫布
    Paint p;
    Thread t;
    Bitmap buffer=null;//二級緩存
    Path mpath;//觸控中的軌跡
    float startX;
    float startY;
    Rect window;
    Rect bufferRect;
    boolean run=false;

    public MyView(Context context) {
        super(context);
        init(context);
        setOnTouchListener(this);
    }

    private void init(Context context){
        holder=getHolder();
        holder.addCallback(this);//勿忘addcallback
        this.main=context;
        p=new Paint();
        p.setAntiAlias(true);//消除鋸齒
        p.setStyle(Paint.Style.STROKE);//設置畫筆風格
        p.setAlpha(255);//畫筆的不透明度
        p.setStrokeWidth((float)2);//設置筆觸寬度
        p.setColor(Color.WHITE);
        mpath=new Path();
        buffer= Bitmap.createBitmap(1440,2256, Bitmap.Config.ARGB_8888);//很重要創建二級緩存的必要方法

        t=new Thread(this);

    }
    @Override
    public void run() {
    while(run){
        canvas=holder.lockCanvas();
        window=canvas.getClipBounds();
        Canvas c=new Canvas(buffer);
        c.drawRect(window,p);
        p.setColor(Color.rgb(new Random().nextInt(255),new Random().nextInt(255),new Random().nextInt(255)));
        c.drawPath(mpath,p);
        bufferRect=new Rect(0,0,buffer.getWidth(),buffer.getHeight());
        canvas.drawBitmap(buffer,bufferRect,window,new Paint());
        holder.unlockCanvasAndPost(canvas);
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();

        }
    }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
     run=true;

        Log.i("Created", "surfaceCreated: ");
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        Log.i("Changed", "surfaceChanged: ");
         t=new Thread(this);
         t.start();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        run=false;
        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Log.i("Destroyed", "surfaceDestroyed: ");
    }
    private void onTouchDown(MotionEvent event){
        startX=event.getX();
        startY=event.getY();
        mpath.reset();
        mpath.moveTo(startX,startY);

    }
    private void onTouchMove(MotionEvent event){
        float touchX=event.getX();
        float touchY=event.getY();

        float dx=Math.abs(touchX-startX);//移動的距離

        float dy =Math.abs(touchY-startX);//移動的距離


        if(dx>3||dy>3){
            float cX=(touchX+startX)/2;
            float cY=(touchY+startY)/2;
            mpath.quadTo(startX, startY, cX, cY);//繪製貝塞爾曲線

            startX=touchX;
            startY=touchY;//改變開始繪製的點
        }//這裏巧妙地進行篩選過於短的移動
    }
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
          this.onTouchDown(event);
                break;
            case MotionEvent.ACTION_MOVE:
          this.onTouchMove(event);
                break;
            case MotionEvent.ACTION_UP:

                break;
        }
        return true;
    }

}





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