android遊戲開發(二)觸屏事件處理

在上一章我們沒有把標題欄和狀態欄給去掉  ,  如果在遊戲中 是不會顯示 顯示標題欄和狀態欄的, 如何去掉瞭, 很簡單,  在mainActivity 的onCreate方法中加入下面兩句 即可  :
 
 requestWindowFeature(Window.FEATURE_NO_TITLE); //設置無標題
 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); //設置全屏
 趕緊去試試吧 , 寫程序就是要 多試 嘻嘻..

圖片有瞭 ,我們怎麼樣才能讓圖片有行為呢 ?  那就需要人機交互瞭,通過觸摸屏讓遊戲具有行為
先在這裡 說明下android 的坐標系
android 的坐標系  左上定點為原點坐標(0,0), 向右為X軸,向下為Y軸 說明這個位置是因為有些遊戲引擎是 以 左下為原點的哦 , 大傢要記住喔,後面如果用引擎的話 也有個概念
下面 我們來看看android 的 觸摸屏事件是怎麼處理的
我們先來分析下 現在的用戶界面 都是通過事件驅動 實現人機交互的,當屏幕的界面接受到事件時 根據不同情況 進行不同的處理 就可以實現人機交互瞭
android 支持的觸摸屏事件有:按下、彈起、移動、雙擊、長按、滑動。
按下、彈起、移動(down、move、up)是簡單的觸摸屏事件 我們本章就來說說這個東東.
而雙擊、長按、滑動、滾動需要根據運動的軌跡來做識別的。在Android中有專門的類去識別,android.view.GestureDetector。 這一塊我們後面的章節 在講

那如何實現呢?
 在Android中任何一個控件和Activity都是間接或者直接繼承於android.view.View。一個View對象可以處理測距、佈局、繪制、焦點變換、滾動條,以及觸屏區域自己表現的按鍵和手勢,因為我們的view 是繼承瞭surfaceView,surfaceView又是繼承view 所以要實現簡單的觸摸屏事件,隻需要重寫父類view 裡面的onTouchEvent 方法就可以實現簡單的觸屏屏事件瞭

下面我們來實現一個功能  用上一章的程序 來實現 把圖片顯示在點擊觸摸屏的地方和圖片能根據手指移動而移動

直接看代碼
 
package yxqz.com;

import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.view.MotionEvent;
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;
 float x=0,y=0;
 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);
  
  //設置焦點 如果不設置焦點的話 在該界面下 點擊觸摸屏是無效的 默認為false
  setFocusable(true);
 }
 
 public void onDraw(Canvas canvas){
  canvas.drawColor(Color.BLACK);
  canvas.drawBitmap(bitmap_role, x-bitmap_role.getWidth()/2, y-bitmap_role.getHeight()/2, null);
 }
    //重寫父類中的 onTouchEvent就可以監聽到  觸摸事件瞭 記住要設置焦點喔
 @Override
 public boolean onTouchEvent(MotionEvent event) {
  if(event.getAction()==MotionEvent.ACTION_DOWN){ //處理屏幕屏點下事件 手指點擊屏幕時觸發
   x=event.getX();
   y=event.getY();
  }else if(event.getAction()==MotionEvent.ACTION_UP){//處理屏幕屏抬起事件  手指離開屏幕時觸發
   
  }else if(event.getAction()==MotionEvent.ACTION_MOVE){//處理移動事件 手指在屏幕上移動時觸發
   x=event.getX();
   y=event.getY();
  }
  return true;  //此處需要返回true 才可以正常處理move事件 詳情見後面的  說明
 
 }
 
 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{
     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();
   }  //每10毫秒刷新一次
   }
  }
 }
}
MainActivity 類  這裡就不貼代碼瞭,因為和上一章一樣 ,很簡單的

onTouchEvent() 返回值 解釋
解釋:
    onTouchEvent(),預設使用Oeverride這個方法,通常情況下去呼叫super.onTouchEvent()並傳回佈林值。但是這裡要註意一點,預設如果去呼叫super.onTouchEvent()則很有可能super裡面並沒做任何事,並且回傳false回來,一旦回傳false回來,很可能後面的event (例如:Action_Move、Action_Up) 都會收不到瞭,所以為瞭確保保後面event能順利收到,要註意是否要直接呼super.TouchEvent()。

 
下一張 我們看看如果使用android的手勢識別

 
最後還要註意一點:在初始化的時候不要忘記setFocusableInTouchMode(true);觸屏模式獲取焦點,比較類似 setFocusable(true);
        ——setFocusable(true);//此方法是用來響應按鍵!如果是自己定義一個繼承自View的類,重新實現onKeyDown方法後,隻有當該View獲得焦點時才會調用onKeyDown方法,Actvity中的onKeyDown方法是當所有控件均沒有處理該按鍵事件時,才會調用.
 

原代碼下載地址:http://up.aiwalls.com/2012/0417/20120417100011952.rar
 

補充點 :
  在android 中使用觸摸屏 在模擬機中 我們的鼠標當點擊一次模擬器屏幕然後釋放,先觸發 ACTION_DOWN 然後 ACTION_UP ;如果是在屏幕上移動那麼才會觸發 ACTION_MOVE 的動作;這個很正常, 但在真機中呢 ,是不是 也是這樣的呢 ?  答案是否定的  如果我們那真機測試的話 流程如下
先觸發 ACTION_DOWN 如果手指不抬起的話 會一直觸發ACTION_MOVE事件(就是不移動也會觸發)  然後 ACTION_UP
原因有兩點:第一點是因為,Android 對於觸屏事件很敏感!第二點:雖然我們的手指感覺是靜止沒有移動,其實事實不是如此!當我們的手指觸摸到手機屏幕上之後,感覺靜止沒動,其實手指在不停的微顫抖震動。 所以才會一直觸發action_move事件  
這樣的情況對我們的程序有什麼影響呢
 
比如我們app線程繪圖時間每次用瞭10ms,當手指觸摸屏幕,這短暫的0.1秒內大概會產生10個左右的MotionEvent ,並且系統會盡可能快的把這些event發給監聽線程, 這樣的話在這一段時間內cpu可能忙於處理onTouchEvent事件 從而造成app 的界面沒有足夠資源去處理,而照成界面刷新一卡一卡的。
那麼我們其實根本用不著按鍵響應這麼多次,而是需要在我們每次繪圖後,或者繪圖前接受一次用戶觸摸事件就OK瞭,這樣能讓幀率不至於下降的太厲害不是麼?!如果我們能把觸屏監聽事件 觸發的事件 給慢下來 不就是可以解決這個問題瞭嗎  ,嘻嘻 就是這麼優化的

 
@Override
public boolean onTouchEvent(MotionEvent event) {
 if(event.getAction()==MotionEvent.ACTION_DOWN){
 }else if(event.getAction()==MotionEvent.ACTION_UP){
 }else if(event.getAction()==MotionEvent.ACTION_MOVE){
 }
         synchronized(this){
           try{
               this.wait(Time);     //讓事件線程休眠 減少觸發次數
            }catch(InterruptedException e){
             e.printStackTrace();
           }        
          }
 return true;
 }
 
 上面的代碼 加到你的onTouch 裡面 但有一點要註意喔 ,上面的線程同步對象使用瞭this ,如果這個類 也被別的類作為同步對象的話 ,可能發生死鎖喔,  如果這個類已經被作為瞭同步對象的話 ,  我們重新初始化的時候 新new 一個對象 作為onTouch的同步對象就可以瞭

 
摘自 android,unity3d

發佈留言

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