Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> 【Android+box2D游戲開發彈弓類游戲】2、游戲界面的搭建和移動游戲場景

【Android+box2D游戲開發彈弓類游戲】2、游戲界面的搭建和移動游戲場景

編輯:Android開發實例

在這一講中,我們介紹如何搭建游戲界面,在游戲界面中加入靜態如片,如何移動游戲場景。

三.創建游戲界面

Android中用於顯示游戲界面的視圖,常用的有View和SurfaceView。SurfaceView是從View基類中派生出來的顯示類SurfaceView和View最本質的區別在於,surfaceView是在一個新起的單獨線程中可以重新繪制畫面而View必須在UI的主線程中更新畫面。

那麼在UI的主線程中更新畫面 可能會引發問題,比如你更新畫面的時間過長,那麼你的主UI線程會被你正在畫的函數阻塞。那麼將無法響應按鍵,觸屏等消息。

當使用surfaceView 由於是在新的線程中更新畫面所以不會阻塞你的UI主線程。但這也帶來了另外一個問題,就是事件同步。比如你觸屏了一下,你需要surfaceView中thread處理,一般就需要有一個event queue的設計來保存touch event,這會稍稍復雜一點,因為涉及到線程同步。所以基於以上,根據游戲特點,一般分成兩類。

1 被動更新畫面的。比如棋類,這種用view就好了。因為畫面的更新是依賴於 onTouch 來更新,可以直接使用 invalidate。 因為這種情況下,這一次Touch和下一次的Touch需要的時間比較長些,不會產生影響。

2 主動更新。比如一個人在一直跑動。這就需要一個單獨的thread不停的重繪人的狀態,避免阻塞main UI thread。所以顯然view不合適,需要surfaceView來控制。

3.Android中的SurfaceView類就是雙緩沖機制。因此,開發游戲時盡量使用SurfaceView而不要使用View,這樣的話效率較高,而且SurfaceView的功能也更加完善。

考慮以上幾點,所以我們選用SurfaceView 來進行游戲開發。

下面創建 基於SurfaceView的游戲界面類MainView.java:

  1. package com.catapultdemo;  
  2.    
  3. import android.content.Context;  
  4. import android.view.SurfaceHolder;  
  5. import android.view.SurfaceHolder.Callback;  
  6. import android.view.SurfaceView;  
  7.    
  8. public class MainView extends SurfaceView implements Callback,Runnable {  
  9.    
  10.     public MainView(Context context) {  
  11.        super(context);  
  12.     }  
  13.     @Override 
  14.     public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {  
  15.        // TODO Auto-generated method stub  
  16.         
  17.     }  
  18.    
  19.     @Override 
  20.     public void surfaceCreated(SurfaceHolder arg0) {  
  21.        // TODO Auto-generated method stub  
  22.         
  23.     }  
  24.    
  25.     @Override 
  26.     public void surfaceDestroyed(SurfaceHolder arg0) {  
  27.        // TODO Auto-generated method stub  
  28.         
  29.     }  
  30.    
  31.     @Override 
  32.     public void run() {  
  33.        // TODO Auto-generated method stub  
  34.         
  35.     }  
  36.    

只要繼承SurfaceView類並實現SurfaceHolder.Callback接口和runnable接口就可以實現一個自定義的SurfaceView了。

SurfaceHolder.Callback在底層的Surface狀態發生變化的時候通知View。

Runnable用來實現多線程

游戲界面已經搭建完成,下面要做的就是讓項目啟動之後,顯示我們的游戲界面,也就是MainView。

自定義draw方法,用後之後話界面使用。為什麼要實現這個方法,會在第四節進行說明。

  1. public void draw()  
  2. {} 

現在可以運行一下項目,看一下運行結果。

可能與想象的有些差別。因為android項目創建完成之後,默認創建一個layout布局文件,用於初始布局。所以我們可以刪除這個布局文件。

刪除布局文件之後項目主文件MainActivity.java會報錯。因為,默認情況下,向界面輸出剛剛刪除的布局文件,然後布局文件已經被我們刪除了。

  1. package com.catapultdemo;  
  2.    
  3. import android.os.Bundle;  
  4. import android.app.Activity;  
  5.    
  6. public class MainActivity extends Activity {  
  7.    
  8.     @Override 
  9.     public void onCreate(Bundle savedInstanceState) {  
  10.         super.onCreate(savedInstanceState);  
  11.         setContentView(R.layout.activity_main);  
  12.     }  

此時將setContentView(R.layout.activity_main);改成 setContentView(new MainView(this));

此行代碼的意思是,讓程序運行,向界面輸出我們的游戲場景界面。接下來再一次運行程序。

我們刪除了布局文件後,顯示出了我們的游戲場景,中間的“hello world”也已經消失的,但是與我們的想象的結果還是有些差距。我們接下來需要去除頭部的狀態欄和應用程序的名稱欄。還要試游戲屏幕變成橫屏。

  1. // 隱去電池等圖標和一切修飾部分(狀態欄部分)  
  2. this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);  
  3. // 隱去標題欄(程序的名字)  
  4. this.requestWindowFeature(Window.FEATURE_NO_TITLE);  
  5. // 游戲界面橫屏  
  6. setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); 

此時屏幕顯示界面已經全部變黑了。此時我們的游戲界面搭建完成了。

四.在游戲場景中加入靜態圖片

現在游戲界面還沒有任何的東西。接下來,我們在游戲場景中加入背景圖片,和一些靜態的物體。由於這些背景和靜態的物體不需要模擬物理場景,所以,之需要在游戲場景中畫出圖片即可。

此前已經創建了繼承自SurfaceView的MainView.java游戲界面類。接下來對方法進行完善和介紹。

 

實現了CallBack接口,重寫了一下方法。

public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){}
//看其名知其義,在surface的大小發生改變時激發
public void surfaceCreated(SurfaceHolder holder){}
//同上,在創建時激發,一般在這裡調用畫圖的線程。
public void surfaceDestroyed(SurfaceHolder holder) {}
//同上,銷毀時激發,一般在這裡將畫圖的線程停止、釋放。

實現了Runnable接口,重寫了run方法。下面介紹一下為什麼要實現Runnable接口並且要重寫run方法:surfaceView有onDraw方法,但是surfaceView不會自己去調用這個方法,所以我們要自己實現 draw方法,並放在run方法內。Runnable實現線程,run方法就是在開辟的線程中無限的去執行。所以我們自己完成的draw方法也可以不斷的執行。這個就是刷屏。

在類中定義一些必要的變量。

  1. private Resources res;  
  2. private SurfaceHolder sfh;     
  3. private Thread th;     
  4. private Canvas canvas;     
  5. private Paint paint;   
  6.    
  7. public MainView(Context context) {     
  8.         super(context);  
  9.         res = this.getResources();  
  10.         sfh = this.getHolder();     
  11.         sfh.addCallback(this);    
  12.         paint = new Paint();     
  13.         paint.setAntiAlias(true);     
  14.         paint.setColor(Color.RED);  
  15. this.setKeepScreenOn(true);// 保持屏幕常亮    

Resources資源變量。可以通過this.getResources()獲取項目中的資源。

 

SurfaceHolder: 它是一個用於控制surface的接口,它提供了控制surface 的大小,格式,上面的像素,即監視其改變的。SurfaceView的getHolder()函數可以獲取SurfaceHolder對象,Surface 就在SurfaceHolder對象內。雖然Surface保存了當前窗口的像素數據,但是在使用過程中是不直接和Surface打交道的,由SurfaceHolder的Canvas lockCanvas()或則Canvas lockCanvas()函數來獲取Canvas對象,通過在Canvas上繪制內容來修改Surface中的數據。如果Surface不可編輯或則尚未創建調用該函數會返回null,在 unlockCanvas() 和 lockCanvas()中Surface的內容是不緩存的,所以需要完全重繪Surface的內容,為了提高效率只重繪變化的部分則可以調用lockCanvas(Rect rect)函數來指定一個rect區域,這樣該區域外的內容會緩存起來。在調用lockCanvas函數獲取Canvas後,SurfaceView會獲取Surface的一個同步鎖直到調用unlockCanvasAndPost(Canvas canvas)函數才釋放該鎖,這裡的同步機制保證在Surface繪制過程中不會被改變(被摧毀、修改)。

Thread:定義線程

Canvas: 定義游戲展示的平台,也就是一個畫布。所有的游戲界面將會在畫布上展示。

Paint: 定義畫筆。 擁有了畫布,我們需要一個畫筆在畫布上進行圖畫。

1. 場景中加入背景

首先要在資源文件中提取圖片文件。

  1. //背景圖片  
  2. background_top = BitmapFactory.decodeResource(res, R.drawable.bg);  
  3. background_bottom = BitmapFactory.decodeResource(res, R.drawable.fg);  
  4. //兩個松鼠圖片  
  5. squirrel_1 = BitmapFactory.decodeResource(res, R.drawable.squirrel_1);  
  6. squirrel_2 = BitmapFactory.decodeResource(res, R.drawable.squirrel_2);  
  7. //發射器底座圖片  
  8. catapult_base_1 = BitmapFactory.decodeResource(res, R.drawable.catapult_base_1);  
  9. catapult_base_2=BitmapFactory.decodeResource(res,R.drawable.catapult_base_2);  
  10.    
  11. 加載了圖片文件之後,定義一個常量FLOOR_HEIGHT。這個是地面的高度,為了能夠更精確的擺放物體。這個高度就是手機屏幕下邊緣到游戲中模擬的地圖的高度。  
  12. private static final float FLOOR_HEIGHT =82f;  
  13.    
  14. 接下來還要定義屏幕的高和寬。這個高和寬指的是手機屏幕可見區域的高和寬,並不是游戲場景中的高和寬。請注意。  
  15. ScreenW = this.getWidth();   
  16. ScreenH = this.getHeight();   
  17.    
  18. 接下來在canvas上畫出這些圖片。  
  19. public void surfaceCreated(SurfaceHolder arg0) {  
  20.        ScreenH = this.getHeight();  
  21.        ScreenW = this.getWidth();  
  22.        thread_flag = true;  
  23.        th = new Thread(this); // 創建線程  
  24.        th.start();   //開啟線程  
  25.     }  
  26.    
  27. 此時需要注意。一定要把th.start()開啟線程這段代碼放到surfaceCreated最後,否則會出現啟動自動退出的bug.  
  28.    
  29. private void draw() {     
  30.         try {     
  31.         canvas = sfh.lockCanvas(); // 得到一個canvas實例     
  32.         if (canvas != null) {  
  33.                 canvas.drawColor(Color.WHITE);// 刷屏  
  34.                 canvas.drawBitmap(background_top, 0-w/2, 0, paint);  
  35.                  
  36.                 canvas.drawBitmap(catapult_base_2,260-w,ScreenH-FLOOR_HEIGHT-catapult_base_2.getHeight()-catapult_base_2.getHeight()/4,paint);  
  37.                 canvas.drawBitmap(catapult_base_1,265-w,ScreenH-FLOOR_HEIGHT-catapult_base_1.getHeight()-catapult_base_1.getHeight()/4,paint);  
  38.                  
  39.                 canvas.drawBitmap(squirrel_1, 50-w, ScreenH-FLOOR_HEIGHT-squirrel_1.getHeight(), paint);  
  40.                 canvas.drawBitmap(squirrel_2, 350-w, ScreenH-FLOOR_HEIGHT-squirrel_2.getHeight(), paint);  
  41.                  
  42.                 canvas.drawBitmap(background_bottom, 0-w, ScreenH-background_bottom.getHeight(), paint);  
  43.          }  
  44.         } catch (Exception ex) {     
  45.         } finally {    
  46.             if (canvas != null)  
  47.                 sfh.unlockCanvasAndPost(canvas);  // 將畫好的畫布提交     
  48.         }     

此時運行程序。就可以看到我們的游戲場景了。但是現實的都是非物理模擬部分。

此時,是不是有些小小的興奮。。。。但是不要怪我潑涼水。現在我們只是把一些圖片拼湊在了一起。其他的什麼都沒有呢。手指滑動屏幕也沒有任何反應。

接下來我們使游戲場景進行移動。

五.移動場景

現在的游戲運行之後,只能顯示一半的場景,接下來實現用手指滑動屏幕移動場景。我們要在MainView.java主類中,復寫View中的onTouchEvent方法。此方法用於檢測觸摸屏事件。

  1. @Override 
  2.     public boolean onTouchEvent(MotionEvent event) {  
  3. return super.onTouchEvent(event);  
  4.     }  
  5. 然後分別在onTouchEvent方法中,實現觸屏 按下,抬起,移動事件。  
  6. public boolean onTouchEvent(MotionEvent event) {  
  7.        if(event.getAction() == MotionEvent.ACTION_DOWN)  
  8.        {}  
  9. else if(event.getAction() == MotionEvent.ACTION_UP)  
  10.        {}  
  11. else if(event.getAction() == MotionEvent.ACTION_MOVE)  
  12.        {}  
  13.        return super.onTouchEvent(event);  
  14.     } 

還需要定義兩個變量。position_X是當按下觸摸屏時,觸摸點在當前游戲場景中的x軸位置,move_X是移動觸摸屏時,移動的偏移量。

  1. private float position_X;  
  2. private float move_X; 

當按下觸摸屏時計算當前的場景中的位置。event.getX()是獲取觸摸點的x軸坐標,這個是相對於屏幕的坐標,所以我們還需要加上move_X偏移量,這樣就能獲取當前觸摸點在游戲場景中的位置。

  1. if(event.getAction() == MotionEvent.ACTION_DOWN)  
  2.     {  
  3.        position_X =move_X+ event.getX();  
  4.     }  
  5. 接下來就完成當手指移動時,move_X偏移量的值。偏移量應該是按下手指時的位置與移動時當前位置的差。  
  6. else if(event.getAction() == MotionEvent.ACTION_MOVE)  
  7.     {  
  8.        move_X = position_X-event.getX();  
  9.     } 

得到了偏移量我之需要在draw方法中,畫游戲界面時對每張圖片的x軸位置進行改變。這樣就能屏幕移動的效果。

例如話背景頭部圖片時,x軸的位置減去偏移量的位置就是移動之後的位置。其他圖片方法一樣。不明白的可以查看第二節,Android游戲坐標系一節。

canvas.drawBitmap(background_top, 0-move_X, 0, paint);

接下來我們可以運行程序查看一下效果。

此時的運行效果可能會很失望,移動觸屏,游戲場景並沒有進行移動。原因是我們只是實現了觸屏方法,但是當前surfaceview不允許對觸屏進行點擊。所以我們需要在MainView構造方法中,添加以下代碼。

setClickable(true);

此時再次運行程序。游戲界面可以進行移動了。

但是經過測試會發現,當移動場景的時候,可能會超出整個游戲場景。如下圖。

這個bug是在有些尴尬啊!!

這是由於我們沒有為偏移量進行限制的原因。

0<Move_X<游戲場景寬度 – 屏幕寬度

游戲場景的寬度也就是背景圖片的寬度。定義一個變量 gameWidth,它的值為背景圖片的寬度。

  1. private float gameWidth;  
  2. gameWidth = background_top.getWidth();  
  3.    
  4. 在觸屏移動時對move_X進行限制。  
  5. else if(event.getAction() == MotionEvent.ACTION_MOVE)  
  6.     {  
  7.        move_X = position_X-event.getX();  
  8.        move_X = move_X<0?0:(move_X>gameWidth-ScreenW?gameWidth-ScreenW:move_X);  
  9.     } 

這樣就能保證屏幕不會超出游戲場景的范圍。

再次運行程序。可以發現,運行正常。

下一章中,我們將要介紹整個游戲的核心部門---》創建游戲世界!

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved